なんとなしにビルド後のCSSファイルを眺めてて、なんで使ってないUIパーツのCSS定義が含まれてるの・・?って思ったのがきっかけ。
こういうこと
たとえば、こういうディレクトリ構成でコンポーネントを書いてたとする。
- components - action-buttons - index.jsx - styles.module.css - page.jsx
コンポーネントの実装はこのように。
// index.jsx import styles from "./styles.module.css"; export const ButtonA = () => <button class={styles.buttonA}>A</button> export const ButtonB = () => <button class={styles.buttonB}>B</button> export const ButtonC = () => <button class={styles.buttonC}>C</button>
+
/* styles.module.css */ .buttonA { /* ... */ } .buttonB { /* ... */ } .buttonC { /* ... */ }
で、使う側はこう。
// page.jsx import { ButtonA } from "@/components/action-buttons"; export const Page = () => ( <> <ButtonA /> </> );
想定していた挙動と実際の挙動
- JS: `ButtonA`のみバンドルされる
- CSS: `.buttonA`の定義のみバンドルされる
という挙動を想定していたけど、実際は、
- JS: `ButtonA`のみバンドルされる
- CSS: `.buttonA`だけでなく、`.buttonB`も`.buttonC`もバンドルされる
という少し残念な感じになってしまう。
仕様らしい
(そこまで自信と確証はないけど)自分の理解では、どうやら現状のViteの仕様らしい。
CSS files cannot be treeshaken with side effects · Issue #4389 · vitejs/vite · GitHub
CSSファイルはすべて副作用あり(グローバルに作用する可能性あり)と判断されるので、そうなっちゃうと。
そして現状では、JSのコンポーネント定義ごとにCSS Modulesのファイルも分けてやるのが一番実直かつ簡単な方法そう。
- components - action-buttons - index.jsx - button-a.jsx - button-a.module.css - button-b.jsx - button-b.module.css - button-c.jsx - button-c.module.css - page.jsx
う〜む、トレードオフ・・。
PurgeCSSとかそういう方向性も考えたけど、CSS Modulesと相性よくないことは目に見えてる。
ViteじゃなくてWebpackなら、頑張ればできるという感じ。
GitHub - markmur/webpack-css-tree-shaking-example: Working example of tree shaking CSS modules
ローダーまみれ。
個人的にはこうなってくるとCSS Modulesやめたくなってくるし、CSSを書きたい気持ちがあるときはおとなしくSvelte一択っていう話になっちゃう。