🌋

NuxtでSvelteコンポーネントを動かしてみる

2021/06/30に公開

note社の記事で、共通コンポーネントにSvelteを使うと聞いて、なるほど面白いと思ってやってみました。
https://note.jp/n/n7f757d7050f6

今回は、Nuxt上でSvelteコンポーネントを動かすまでをやっていきます。
上記の記事にもある通り、通常Svelteを動かすには以下のようにtargetnewすると動くのですが、SSRでは動きません。

import App from './App.svelte';

const app = new App({
	target: document.body,
	props: {
		name: 'world'
	}
});

export default app;

svelte-adapterを利用することで、SSRでも動きましたが、Stelteコンポーネントに子要素やslotsは渡せないようなので、注意が必要です。
https://github.com/pngwn/svelte-adapter

インストールする

Svelte本体、ローダー、VueやReactの上でSvelteを動かすアダプターの3つをインストール。

Bash
yarn add svelte svelte-loader svelte-adapter

webpack configとsvelte-adapterを設定する

公式のtemplate-webpackを参考にNuxtで、webpack configの拡張設定をします。
https://github.com/sveltejs/template-webpack/blob/master/webpack.config.js

nuxt.config.js
export default {
	build: {
	    // svelte-adapter
	    transpile: ['svelte-adapter/vue'],

	    // Svelte
	    extend(config, { isDev, isClient }) {
	      config.module.rules.push({
		test: /\.svelte$/,
		loader: 'svelte-loader',
		options: {
		  emitCss: true,
		}
	      })
	    }
	}
}

svelte-adapter

アダプターはBabelでトランスパイルするよう設定します。
設定せずに使うと、Cannot use import statement outside a moduleエラーを吐いてしまいます。

webpack configの拡張設定

拡張子.svelteにsvelte-loaderが読み込まれる設定をbuild内に追加します。

optionのemitCss設定は、コンポーネントの<style>タグを別のCSSファイルで生成するか否かの設定です。

svelte-loaderのReadmeを見るとfalseの場合、スタイルはJSを利用して指定されるが、JSが重くなるのと、スタイルの読み込みに悪影響する可能性がある、そしてコンテンツセキュリティーポリシー(CSP)に違反を引き起こす可能性もあるとあります。

trueであれば、別ファイルでCSSが生成され、そういったリスクもないので、trueが推奨されているようです。

https://github.com/sveltejs/svelte-loader

アダプターを利用したコンポーネント作成

componentsディレクトリ内にSvelteコンポーネントを作成します。

Bash
touch components/SvelteTest.svelte

中身は単純にHello worldを表示させるだけ。

components/SvelteTest.svelte
<script>
	let name = 'world';
</script>

<h1>Hello {name}!</h1>

<style>
    h1 {
        color: blue;
    }
</style>

コンポーネントはグローバルで読み込みたいので、pluginsを使ってグローバル化。

Bash
touch plugins/common-components.js

アダプターを利用して、インポートします。

plugins/common-components.js
import Vue from 'vue'
import toVue from 'svelte-adapter/vue'
import SvelteTest from '~/components/SvelteTest.svelte'

Vue.component('SvelteTest', toVue(SvelteTest))

プラグインを読み込ませます。

nuxt.config.js
export default {
  plugins: [
    'plugins/common-components',
  ],
}

コンポーネントを表示させると...

pages/index.vue
<template>
  <div>
    <SvelteTest />
  </div>
</template>

無事に表示されました!

Discussion