EmotionとSvelteを組み合わせる場合の設定とうまくいかない部分とか
CSS in JSライブラリのEmotionにはReact版とFramework agnostic版がある。なのでReact以外のビューライブラリと組み合わせたりバニラJSでも使ったりできるけど、Babelプラグインが実質必須なのでSingle File Componentsみたいなスタイルとは相性が悪い。
<script> import { css } from "emotion"; </script> <h1 class={css({ color: "pink" })}>Hello!</h1>
EmotionのBabelプラグインはコードの最適化に加えてCSSのソースマップ生成を行う。Emotionが生成するクラス名はランダムなハッシュ値になって可読性がないので、デバッグするにはソースマップがかなり重要になる。Babelプラグインが正しく機能すれば開発者ツールで検出した宣言ブロックからソースコード内の定義元が参照できるようになる。
一方でSvelteのコンポーネントはそのままではBabelでパースできないので、このBabelプラグインを使うには次のいずれかの方法を取るしかない:
- svelte-loaderあるいはrollup-plugin-svelteによってJavaScriptに変換された後にBabelを適用する
- Svelte Preprocessによって
script
要素内のみにBabelを適用する
1の場合、JavaScriptにコンパイルされた後のソースコードを基準にしてソースマップを生成するので、ユーザーが実際に編集するファイルと別のものを参照してしまう。
2の場合、script
要素内のコードについては正しくソースマップが生成されるが、テンプレートにはBabelが適用されないので、テンプレートに直接記述した定義にはソースマップが生成されない。この場合テンプレート内の最適化も行われない。
またいずれにしてもSvelteコンポーネントの外でスタイルを定義してimport
すればソースマップは正しく生成される。
import { css } from "emotion"; export const hello = css({ color: "pink" });
<script> import { hello } from "./styles"; </script> <h1 class={hello}>Hello!</h1>
しかしこの使い方だとインラインスタイル風に書きたいというモチベーションが満たせないので自分としてはあまり意味がない。同じ理由でscript
要素内にすべて定義してしまうのも無い。
そのためどちらかというと1の方がマシという結論。ソースマップがまったく無いよりは良いし、最適化も漏れなく適用させたい。webpack.config.js
はこんな感じ:
module: { rules: [ ... { test: /\.svelte$/, use: [ { loader: "babel-loader", options: { plugins: ["emotion"], }, }, { loader: "svelte-loader", }, ], }, ... ], },
またVS Codeでは、Babel JavaScriptやvscode-styled-componentsをインストールすることでTemplate literals内に記述したCSSがシンタックスハイライトされたり補完が効くようになったりするが、Svelteコンポーネントのテンプレート中ではそれが有効にならない。script
要素内では動く。
代わりにObject Stylesで記述すると特別なプラグインを足さずともまともに書けるようになるし、Emotionの型定義ファイルからプロパティ名や値を補完してくれる。最低限Svelte for VS Codeは必要。