This is a cache of https://qiita.com/terrierscript/items/0574ab1ef358fecb55b9. It is a snapshot of the page at 2019-08-20T05:29:35.265+0000.
なんとなくで理解しないWebpackのCSS周辺 - Qiita
Edited at

なんとなくで理解しないWebpackのCSS周辺

More than 1 year has passed since last update.

CSS in JSに夢を見たが、なかなか一筋縄では行かなかったので1、webpackにおけるCSSと本気で向き合ってみた。

しかしまだ理解が甘いところがあったのでloader, pluginまわりの関係性を整理した。


(前置き)webpackの基礎情報

css関連の本題にはいる前に、webpackの基礎を再確認する。


Webpackの特徴

webpackの特徴的な事項として、CSSや画像など、javascriptでないデータも基本的に全てをjavascriptで扱ってしまう、という事が挙げられる。

同等の対抗として挙げられるbrowserifyやrollupは、あくまでも「javascriptのmodule解決」にフォーカスしているのに対して、webpackは全く違う方向を向いている


loaderとpluginの違い

結構あやふやに扱っていたが、上記のwebpackの基本部分を明確にして考えると考えやすい。


  • loader


    • 「どんなものでも最終的な出力をjavascriptで実行できる形に変換する」もの。

    • 受け付けるinputは、画像やらCSSやらなんでも良い

    • outputは、必ずjavascript。


      • css-loader -> CSSを、javascriptのコードとして扱わせる

      • url-loader -> 画像データなどを、base64エンコードして、javascriptで扱えるようにする





  • plugin


    • 「何らかの別な出力や、作用をもたらすもの」

    • inputもoutputも制限は無いので、ある意味「loader以外の色々」と言っても語弊は無さそう


      • javascriptファイル以外の書き出し

      • コンソール出力の加工






CSSをwebpackで扱うことの利点と欠点

下記が挙げられる。特に欠点については、注意深く取り扱うべきものがある


  • 利点


    • ビルドツールの単一化。javascriptのビルドとcssのビルドを一元化しやすい。

    • 既存の資産を活かしやすい

    • CSS modulesなど、style情報とjavascriptの結びつきを強めた状態を無理なく作りやすい


      • モジュール密度を高めた開発が可能になる





  • 欠点


    • webpackでしか解決できない機能が多く、依存度が高くなる。


      • 脱webpackなどの時代が来た時に負債化しやすい

      • browserify向けのcss-modulesifyというのもあるが、highly experimentalな上に、browserifyとしてもイレギュラーな扱いになる。



    • webpack以外のエコシステムでエラーになる


      • テストツールなどでよく問題になる。


        • mochaなどでは、compilerを工夫するなど、そこそこ頑張らないと解決出来ない事がある。



      • 比較的モダンなテストツールの場合、標準的に対応オプションを明示してくれている場合もある





    • loaderは内部的にかなり無茶をやっている場合があるので、注意深く選定する必要がある。



総じて、利点としてはデザイナーフレンドリーで、かつCSSの問題点を解決する手法を得る事が出来る。

対して、欠点としてはwebpackが独特であるが故にもたらされる事象が多い。


(本題) CSS周りの様々なloader, pluginの責務分割

だいたい下記のような責務に分割出来る。

一番左は便宜的に通し番号をつけている。

#
役割
代表的なloader,plugin
必須?

A
CSSのデータ -> HTML上で読み込めるものにする
style-loader, extract-text-webpack-plugin
yes

B
CSS -> javascriptで読み込めるという状態にする役割
css-loader, raw-loader
yes

C
メタCSS言語をコンパイルする役割
sass-loader, stylus-loader, less-loader
no

D
CSSの事後処理を担当するもの
postcss-loader
no

一般的な場合、それぞれの分類をから一つずつ選択することになる。

例えば一般的な組み合わせで言えば、style-loader(A) + css-loader(B) + postcss-loader(D) や、extract-text-webpack-plugin(A) + raw-loader(B) + sass-loader(C)など。

逆に、同じ役割のものを複数扱う場合というのはレアケースだろう。

AとBはCSSの利用にあたって必要になる。

Dは技術的には必須ではないが、実務上はほぼ必須と言っても良い。

以下、それぞれの分類を細かめに砕いていく


A. CSSのデータ -> HTML上で読み込めるものにする

対象は以下


  • style-loader

  • extract-text-webpack-plugin

大本の所になる、CSSを「扱える状態にする」もの。

CSSの内容そのものについては全く関与しない。


style-loaderのやっていること

style-loaderは、loaderなので、前述通り最終的なOutputはjavascriptである。

どのようなコードを出力するのかを端的に言えば「CSSファイル<style>タグを自動で生成して、その中に指定のCSSのソースを入れ込むjavascript」という歯切れの悪い感じになる。

利点として、下記のようなことが考えられる。


  • CSSについて、javascriptのみで完結することができる

  • 更新した場合のcache busterやdigestについて気にしなくて良い。


    • rails上であればsprocketsとの連携なども気にしなくてよくなる。



  • Hot module reloadやsource mapなどを別途対応せずとも対応してくれる

逆に欠点として、下記のような部分が存在する。ここが問題となる場合は、extract-text-webpack-pluginを利用すると良い



  • <style>を差し込む形なので、レガシーなCSSの置き換えが目的であったり、SSRを前提としている場合は、style適用に遅延が発生して、画面がチラつくなどの問題が出やすい。

  • 共通のCSSを色々なjavascriptから読み込んでいる場合、ブラウザキャッシュが効かないので非効率になる

  • CSSとjavascriptがワンパックなので、それぞれを並列で読み込んだ場合より遅くなりやすい


    • CSSが肥大化してくるとより顕著になってくることが考えられる




extract-text-webpack-pluginのやっていること

extract-text-webpack-pluginは、「loaderが実行した結果を別途textに吐き出す」というもの。(CSSに限ったpluginではない)。loaderではなくpluginとして提供されているのも、最終的にjavascriptへ変換するものではないからだ。

style-loaderでは「javascriptとして吐き出す」だったのに対し、こちらでは変換されたCSSファイルを「通常のCSSファイル」として吐き出すことが出来る。

当然だが、CSSの読み込みそのものは別に管理してくれないので、自分で<link>タグの読み込みやdigestは別途管理することになる。

この辺りをハンドリングしたい場合や、style-loaderでの欠点を解消しなければならない場合は利用価値が高い。


B. CSS -> javascriptで読み込めるという状態にする役割

対象は以下


  • css-loader

  • raw-loader

import foo from 'foo.css'require('foo.css')といった、「javascriptからCSSを読む」という、webpack独特なやり方を解決する役割として、css-loaderやraw-loaderが存在する。

あくまでも「javascript上で扱えるようにする」という部分のみを責務としている。


css-loader

cssをloadする場合、そこそこ高機能になるのがcss-loader

基本はこちらを使う方がスタンダードと思われる。


  • url解決


  • @import解決

  • css-moudles対応(local/globalスコープなど)

この他にもかなり色々機能を持っている。(そのうち別途まとめたい)


raw-loader

css-loaderは色々やってくれるが、「いや、単にCSSをjs上で解決出来てくれればそれでいいんだ」という人にはraw-loaderで十分になる。

raw-loaderはごくシンプルに

module.exports = JSON.stringify(対象のファイル)

ということをやっているだけである。

extract-text-webpack-plugin同様、これもcssを主目的としたloaderではないので、対象は何でも利用できる。


C. メタCSS言語をコンパイルする役割

対象は以下。この他にもおそらく存在するが、ここでは代表的なものを取り上げる


  • sass-loader

  • stylus-loader

  • less-loader

いわゆるメタCSS言語であるscssやstylus, less。

これらの変換には、別途それぞれのloaderが存在する。

このあたりを利用する場合、gulpなどで変換して「cssファイルにする」、というのが馴染み深い手法だが、loaderを利用した場合、あくまでも「cssの文字列」にしているだけである。

less-loaderの文頭にはこんな感じでの記載が存在している。

var css = require("!raw!less!./file.less");

// => returns compiled css code from file.less, resolves imports
var css = require("!css!less!./file.less");
// => returns compiled css code from file.less, resolves imports and url(...)s

この場合、「CSSとして変換されたlessファイルcssというjavascript上の変数に格納している」という処理をしているに過ぎない。

そのため、スタイルシートとして利用するには

require("!style!css!less!./file.less");

のようにstyle-loaderを利用してHTML上でスタイルシートとして扱えるようにする必要がある。

前述で述べた通りstyle-loaderextract-text-webpack-pluginでも代替可能なものである。extract-text-webpack-pluginを利用した場合はgulpを利用するのと近い形でcssをファイルとして取得出来る


D. CSSの事後処理を担当するもの

下記が対象。


  • postcss-loader

autoprefixer-loaderというのも存在するが、こちらはdeprecated。

postcssについては昨今ドキュメントや利用事例も増加してきているので、多くは記載しないが、「CSSの事後処理」と位置づけられる工程となる。

様々な plugin の組み合わせから、下記のようなことを選択的に行える。



  • -webkit,-mozなどの、prefixerと呼ばれる接頭辞の追加(autoprefixer)


    • 昨今の開発ではほぼ必須。



  • 様々な最新のCSS構文を後方互換的に変換(cssnext)


    • javascriptにおけるbabelに近い

    • 但し、cssnextで取り扱われているものの中には、少々将来的に取り込まれる可能性が低いのでは?と思えるものも一部存在するので、取扱いには要注意



postcssのプラグインの中には、postcss-assetsなど画像を扱うものもあるが、これらはwebpack上で扱う場合には役割が重複してしまうので、あまり出番は無いと思われる。





  1. 既存のコードにはバッチリだったが、新規開発にはちょっと向いてない部分が多かった。