Webpackのcss系のloader周りを調べてみた
はじめに
Webpackを設定する中でcssファイルに対するloader(css-loader
とかstyle-loader
、raw-loader
など)がよく利用されています。
しかし、それぞれの役割、挙動が自分の中であやふやだったのでそれぞれ調べた時の結果などをブログにまとめました。
loader
css-loader
一言で言えばurlやimport/require()の解決を行いオブジェクトとして生成するloader。
まず最初に以下のようなwebpack設定ファイルで*.css
ファイルがどのように解決されるかを調べます。
(module部分のみ記述) (ts-loaderを用いてトランスパイルしていますが、省略)
module: { rules: [ { test: /\.css$/, use: [ { loader: "css-loader", options: { modules: true, }, }, ], }, ], }
そしてこのようなファイルを用意してwebpackでバンドルさせます。
import style from "./index.module.css"; console.log(style);
.test { color: blue; }
これらの出力は以下のようになります。
0: (4) ["./src/index.module.css", "._34TPoweJRFabqRaBcU6yIt {↵ color: blue;↵}↵", "", {…}] i: ƒ (modules, mediaQuery, dedupe) locals: {test: "_34TPoweJRFabqRaBcU6yIt"} toString: ƒ toString()
css-loaderによって生成されたこのオブジェクトは様々な用途で利用されます。
例えば
- style-loaderによるstyleタグの生成
- style-loaderによるCSS-Moduleの実現
- mini-css-extract-pluginによるcssファイル生成
公式のGetting Startedでも例があるようにcss-loader
はstyle-loader
などといった様々なloaderと併用されることが多そうです。
style-loader
一言で言えば、cssをdomに注入する役割を持つloader。
上記で述べたcss-loader
と併用した以下のようなwebpack設定ファイルを用意します。
module: { rules: [ { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true, }, }, ], }, ], }
加えて適当にreact + typescriptでクリックするとalert()
が実行されるindex.tsx
ファイルとcssファイルを用意します
import React from "react"; import ReactDom from "react-dom"; import style from "./index.module.css"; type AlertButtonProps = { message: string; }; const AlertButton: React.FC<AlertButtonProps> = ({ message }) => ( <button className={style.test} type="button" onClick={() => alert(message)}> Let's alert </button> ); ReactDom.render(<AlertButton message="hi" />, document.getElementById("app"));
.test { color: blue; }
これを実行すると表示されたAlertButton
にハッシュされた_34TPoweJRFabqRaBcU6yIt
のようなクラスが付与&styleタグの生成が行われ、CSS-Moduleが実現できます。
CSS-Module意外にも普通にcssを生成する方法もあったり、
style-loaderのoptions.injectTypeを'linkTag'に設定し、file-loaderと併用することでlinkタグによるスタイルの適用を行なったりすることもできます。
raw-loader
一言で言うと、ファイルを文字列としてimportできるようにするloaderです。
公式ではtxtファイルを文字列として読み取る例が用意されています。
raw-loader
でcssファイルを文字列として読み込み & style-loader
でDOMに反映と言う流れでも上記と同じことができるようです。
ただ、文字列として読み取っているだけなのでCSS-Moduleなどは当然無理です。
{ test: /\.css$/, use: [ { loader: "style-loader", }, { loader: "raw-loader", }, ], }
mini-css-extract-plugin
一言で言えば、cssをそれぞれのファイルに生成するプラグイン
下記のようなwebpack設定フィアルでビルドすると今までの処理結果とは異なりcssが別で生成され、linkタグにて読み込まれるようになります。
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
{ test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, }, { loader: "css-loader" }, ], }
react-scriptsのwebpack.config.jsでは以下のような*.cssファイルに対する設定が行われています。(一部抜粋)
const loaders = [ isEnvDevelopment && require.resolve('style-loader'), isEnvProduction && { loader: MiniCssExtractPlugin.loader, // css is located in `static/css`, use '../../' to locate index.html folder // in production `paths.publicUrlOrPath` can be a relative path options: paths.publicUrlOrPath.startsWith('.') ? { publicPath: '../../' } : {}, }, { loader: require.resolve('css-loader'), options: cssOptions, }
develop環境だとstyle-loaderを利用、production環境だとmini-css-extract-pluginを利用しています。
ここはおそらくパフォーマンスを考慮して切り替えていると思われるのですが、今後自分の方で試してみたいと思います。
まとめ
webpackのcss周りについての設定の理解を多少深めることができたと思います。
これはそもそも「CSS-Moduleにおけるモジュールの構築順が書き換わってリファクタリングしたときに意図せぬスタイルが適用されたりするので純粋なBEMの方がよさそう...」のような記事をみたのがきっかけでした。
CSS-in-JSなのかCSS-Moduleなのか、手段は多くあるのでそれぞれ理解を深めていきたいです。
以上です。