かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

webpack css-loader と style-loader の違いについて学んだ

CSS を webpack で扱う時にセットで使いがちな css-loader と style-loader の違いをちゃんと理解してなかったので CSS Modules について考えるに当たって調べ直したのでメモ

webpack の loader の処理順

e.g. SCSS を webpack で扱う際の設定

// webpack.config.js
module.exports = {
  // ...
  rules: [
    test: /\.(scss|sass)$/,
    use: [
      'style-loader', 
      'css-loader',
      'sass-loader',
    ]
  ],
}

webpack の loader は下から順番に実行するので下記のような順で処理が実行される

sass-loader > scss-loader > style-loader

style-loader

Inject CSS into the DOM.
cf. GitHub - webpack-contrib/style-loader: Style Loader

webpack のページのサンプルを見るとわかりやすかったです。
雑にまとめると style-loaderCSS を HTML の head タグ内にインラインスタイル (<style></style> ) を出力する loader

css-loader

The css-loader interprets @import and url() like import/require() and will resolve them. cf. GitHub - webpack-contrib/css-loader: CSS Loader

css-loader は機能が多いのですが、これも雑にまとめると
css-loaderCSS を JS で扱える形に変換する loader

SCSS / SASS の変換 rules を見返すと…

// webpack.config.js
module.exports = {
  // ...
  rules: [
    test: /\.(scss|sass)$/,
    use: [
      'style-loader', 
      'css-loader',
      'sass-loader',
    ]
  ],
}

この設定は次のように変換をしていると読み取ることができました。

  1. sass-loader で scss / sass を css に変換
  2. scss-loader で JS で読み込める形式に変換
  3. style-loader で JS として読み込んだ CSS を HTML の にインラインスタイルとして挿入

style-loader をもう少し詳しく

style-loader は HTML の <head> 内にインラインスタイルを出力するけど、build しても CSS ファイルは作成されず bundle.js ファイルが作成されるだけです。

つまり、style-loader は css-loader で JS で扱える形にした CSS.js ファイルの中に持っておいて HTML が読み込まれ script が実行された時に <style> タグを生成して HTML の <head> に挿入するという処理を行うということのようです。

CSS ファイルとして出力したい場合は MiniCssExtractPlugin を使う

MiniCssExtractPlugin | webpack

This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.
cf. GitHub - webpack-contrib/mini-css-extract-plugin: Lightweight CSS extraction plugin

このプラグインを使えば CSS ファイルが生成され <link> で読み込まれるようになります。

// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
  // ...
  rules: [
    test: /\.(scss|sass)$/,
    use: [
      MiniCssExtractPlugin.loader,
      'css-loader',
      'sass-loader', 
    ]
  ],
  plugins: [
    // plugins 内で出力されるファイル形式などのオプションを指定できる
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css',
    }),
  ],
}

この設定で build すると、CSS のファイルが生成されるようになりました!

webpack-dev-server を使う dev モードでも onMemory で CSS ファイルが作成され問題なく動作するので、 MiniCssExtractPlugin を使うのであれば style-loader は不要。

build した時だけ MiniCssExtractPlugin で CSS ファイルを生成したい場合

webpack の mode を利用して使用する loader を分岐させればOK!
コマンドから --mode development の様に送る場合は argv.mode で値を取得できる。

// webpack.modules.js
module.export = function (env, argv) {
  const mode = process.env.NODE_ENV || argv.mode || 'development';
  const isProduction = mode === 'production';

  return {
    //...
     module: {
      rules: [
        {
          test: /\.(scss|sass)$/,
          use: [
            !isProduction && 'style-loader',
            isProduction && MiniCssExtractPlugin.loader,
            'css-loader',
            'sass-loader', 
          ].filter(Boolean), // false が残るとエラーになるので filter(Boolean) で除去する
        }
      //...
  };
}

₍ ᐢ. ̫ .ᐢ ₎❤︎

まとめと感想

あいまいだった style-loader と css-loader の違いがクリアになった気がします。

  • インラインスタイルを JS から出力したい場合は style-loader
  • .css ファイルを生成して読み込ませたい場合は MiniCssExtractPlugin

という使い分けをすれば良さそうです。

create-react-app を eject して webpack.config.js を見てみると development の時は style-loader を使い production でビルドする際は MiniCssExtractPlugin を使うような設定になっていました。
Hot Reload で頻繁にファイルが変わる development では都度 css ファイルを生成しない style-loader の方が高速で、実際のプロダクトの状態では css ファイルを読み込ませる方がパフォーマンスが良いのからそうしているのかな〜と感じました。

個人的にも JS から <style> タグを生成するのは script でその他の処理がブロックされている状態で CSS が出力され script の実行を待ってレイアウトのレンダリングがされるというイメージです。
プロジェクトが大きくなると JS も CSS も大きくなるでしょうから、CSSbundle.js に含まれているとそれだけファイルサイズが大きくなり script の実行で処理がブロックされる時間が長くなりそうな気がします。(測定したわけではないのであくまでイメージですが…)


[参考]

速習 webpack 第2版 速習シリーズ

速習 webpack 第2版 速習シリーズ

毛穴撫子 お米のマスク 10枚入

毛穴撫子 お米のマスク 10枚入

  • メディア: ヘルスケア&ケア用品

Ruby GiHub のブランチをブランチを指定して gem で読み込ませたい

Gemfile で読み込んでいる GitHubリポジトリをPR出してるブランチで問題ないか確認したい

Gemfile

gem 'aikatsu', git: "git://github.com/<username>/aikatsu.git"

branch: <branch_name> で指定すればOK

Gemfile

gem 'aikatsu', git: "git://github.com/<username>/aikatsu.git",
  branch: 'planet'

これで bundle install すれば OK

$ bundle install
# …
Using aikatsu 1.0.0
  from git://github.com/<username>/aikatsu.git (at planet@<hash>)

ブランチ指定されているログが流れてきたら OK

おまけ

作業してた環境は プライベートリポジトリで token を発行して https://<token>:x-oauth-basic@github.com/<username>/<reponame>.gt の形になっていて、

git+https://<token>:x-oauth-basic@github.com/<username>/<reponame>#<tagname>

ように最後の <tagname> の箇所をブランチ名にすることができるという記事を見つたのですが、Gemfile ではリポジトリを見つけられないというエラーになってしまい上手く行かず、branch: <branch_name> を指定する方法で意図した通りに動作させることができました。 (よく見たらこの記事 npm でした… js とで指定方法が違うのでしょうか…ナゾ)


 

Ruby 関係時々触るくらいなので全然ちからが付かない…
おわり。


[参考]

ウェス・アンダーソンの風景(仮) Accidentally Wes Anderson

ウェス・アンダーソンの風景(仮) Accidentally Wes Anderson

  • 発売日: 2020/12/18
  • メディア: 単行本(ソフトカバー)

アイカツ!プラネット!も楽しみ!!

React create-react-app で作ったアプリの bundle サイズを確認したい

SPA の速度カイゼンやパッケージやファイルのサイズを可視化してみたい事が多々あります。

今回は create-react-app で作成したアプリでバンドルサイズを可視化してみたのメモ。

create-react-app v3 以降は --stats オプションが廃止され webpack bundle analyzer は使えなくなっていた

バンドルサイズ可視化で調べると出てくる webpack-bundle-analyzer を使う方法を見つけて試していましした。

$ yarn build --state && npx webpack-bundle-analyzer ./build/bundle-stats.json
# …
Compiled successfully.
#...
Couldn't read webpack bundle stats from "/Users/kikiki/app/frontend/build/bundle-stats.json":
Error: ENOENT: no such file or directory, open '/Users/kikiki/app/frontend/build/bundle-stats.json'
error Command failed with exit code 1.

--state オプションを付けてビルドすれば ./build/bundle-stats.json が作成されて webpack-bundle-analyzer で見ることができるというものでしたが、create-react-app v3 以降では --status オプションが廃止されていて bundle-stats.json が作成されなくなっていました。
ejict して webpack の設定自前で弄りたくはない…

Source map explorer を使う

v3 以降では公式に記載されている Source map explorer を使うのが良さそうです。

パッケージをインストール

$yarn add -D source-map-explorer

package.json に npm scripts を追加

"scripts": {
  "start": "PORT=8888 react-scripts start",
  "build": "react-scripts build",
  // ...
  "analyze": "yarn build && source-map-explorer 'build/static/js/*.js'"
}

実行

$ yarn analyze

ビルドが完了したら build/static/js/ 内に出力された js ファイルを元に解析した結果をブラウザで表示してくれます。

f:id:kikiki-kiki:20201019162901p:plain source-map-explorer は見た目がめちゃめちゃシンプルです

それでも webpack-bundle-analyzer が使いたい!

source-map-explorer は見た目がめちゃめちゃシンプルなのでやっぱり webpack-bundle-analyzer を使いたい場合

cra-bundle-analyzer というパッケージを使えば eject して webpack の設定を自分で頑張らなくても webpack-bundle-analyzer が使えるようになるようです。

$ yarn add -D cra-bundle-analyzer
$ npx cra-bundle-analyzer

f:id:kikiki-kiki:20201019162929p:plain

解析が終わると./build/report.html という静的ファイルが作成されて自動的にブラウザで開くようになっており、そこでカラフルな webpack-bundle-analyzer の表示で見ることができるようになりました!
サイズの大きな webpack-bundle-analyzer はlocalnにはインストールせずに npx で実行しているみたいなので、カラフルな表示で見たい場合は cra-bundle-analyzer を使うと良さそうです!

おわり


[参考]