StorybookをViteで立ち上げる ~Emotionを添えて~
こんにちわ!ちょっと株式会社 フロントエンドエンジニアのhanetsukiです。
弊社で最近、Next.js環境にStorybookの導入をする機会があったので忘備録的にナレッジを残したいと思います。
前置き
Storybookとは、
コンポーネント駆動型UIをより高速に構築する
ためのツールらしいです。
コンポーネント単位で開発する際にデザイナーとエンジニアが双方向に確認しあえるカタログツールなものであると私は認識しています。
近年では、Storybook上でホワイトボックステストを実施できるようになったりと機能面も充実してきました。
もうただのカタログツールとは言えなくなってきましたね。
どうしてViteで立ち上げるのか、
なぜ今回、Viteを採用しようと思ったのか、要因になったのはプロジェクトの規模感と開発体験です。
大規模プロジェクトでは、数千ものモジュールが含まれることも珍しくありません。JavaScript ベースのツールを使用していては、いずれパフォーマンスのボトルネックにぶつかります: 開発サーバを起動するのにやたらと長く待つこともあります(数分かかることさえ!)。また、HMR を利用していても、ファイル編集がブラウザに反映されるまで数秒かかることもあります。フィードバックの遅さが継続することは、開発者の生産性や幸福度に大きな影響を与える可能性があります。Vite では新しいエコシステムの進歩を活用し、これらの問題を解決することに取り組んでいます: ブラウザのネイティブ ES モジュールや、ネイティブにコンパイルされる言語で書かれた先進的な JavaScript ツールの利用です。
とあるように、Viteでは開発者の生産性や幸福度などに問題を抱え、パフォーマンス改善に注力しています。
実際に筆者がwebpack5とViteを比較したところ6, 7秒ほどStorybookが立ち上がるのに時間がかかりました。(コンポーネントが増えれば増えるほどその差は開いていくと思うとゾッとします...)
何故Emotionを添えたのか、
プロジェクトで使おうという方針に決まったのです。
話は以上です。(私は、Emotion好きです。)
前置きが長くなりました、導入に取り掛かりましょう。
導入方法
Storybookを導入する
Viteベースの環境でStorybookを導入するコマンドを、導入したいプロジェクトルートで実行します。
npx storybook init --builder vite
途中、Storybookのeslint-pluginの導入するか尋ねられますが、任意で選択してください。今回は、Noと答えました。
しばらく待つと、.storybook
フォルダとsrc/stories
フォルダが作成されるかと思います。
それぞれ、.storybookフォルダはStorybookの設定ファイル。storiesフォルダには、サンプルファイルが含まれています。
今回はNext.js環境なので依存環境であるViteをインストールします。
yarn add -D vite
環境は整いました!下記コマンドを実行してStorybookを起動してみましょう。
yarn storybook
無事起動できると、welcome to Storybook が私たちを出迎えてくれます。
ここまでで、StorybookとViteを使った環境構築は完了です。
Emotionの適応
ここからは、Emotionの適応です。
よく見かける css modules や PureCSS であれば、複雑なカスタマイズは不要のようなのですが、
CSS in JSを利用する際にはちゃんとコンパイラーに通してあげないと適応されないなどがあるようです。
今回、適応するにあたり困ったのはこのエラーでした。
You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop).
このエラーはコンソールに表示されるのではなく、Emotionを適応しているコンポーネントのclassNameにdevtoopで確認した時に現れます。
これをVite環境で解決する方法はコチラのissueに記されていました。
今回の環境下で実施するのは、.storybook/main.js
の編集です。早速適応していきます。
const react = require("@vitejs/plugin-react");
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions"
],
"framework": "@storybook/react",
"core": {
"builder": "@storybook/builder-vite"
},
"features": {
"storyStoreV7": true
},
async viteFinal(config) {
config.plugins = config.plugins.filter(
(plugin) =>
!(Array.isArray(plugin) && plugin[0]?.name.includes("vite:react"))
);
config.plugins.push(
react({
exclude: [/\.stories\.(t|j)sx?$/, /node_modules/],
jsxImportSource: "@emotion/react",
babel: {
plugins: ["@emotion/babel-plugin"],
},
})
);
config.esbuild = {
// Fixed: [vite] warning: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
// https://github.com/vitejs/vite/issues/8644
logOverride: { 'this-is-undefined-in-esm': 'silent' }
}
return config;
}
}
こちらの設定を適応することで、Emotionのスタイルが反映されたStorybookを開くことができたかと思います!
最後に
ナレッジが少ない中解決策を見つけるのは大変でしたが、「同じ問題にぶち当たる人のためにissueを出しておきます」と書いてあった今回のissueには涙しました。
公式により近いところに状態のいい情報は揃っているんだなぁと改めて感じました。(builder-viteのissue)
今後こういったコンポーネント開発体制はどんどん増えていくと思います。既に多くのプロジェクトが取り組んでいることかもしれません、その日本語ナレッジの一つにこの記事もなってくれればと思います。
では、これにて
ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion