Help us understand the problem. What is going on with this article?

style-loaderを使ってstylesheetをrequireする

More than 3 years have passed since last update.

モバイルファクトリー Advent Calender 12日目の記事です。
今日はフロントエンドエンジニアの@nekobatoがお送りします。

style-loaderの紹介

最近、browserifyをwebpackが上回った調査もあったりして、webpackがようやく浸透してきたなーという印象です。

htmlをrequireしている人も多くなっている(SPA & React.jsのおかげ?)印象ですが、stylesheetをrequireしているプロジェクトはあまり見かけません。

なので、style-loader便利なんだよという紹介をしようと思います。

styleをrequireするstyle-loader

css-loaderと組み合わせることが推奨されており、Javascriptのコード中でstylesheetをrequire出来るようになります。

使い方

webpack.config.js
module: {
  loaders: [
    {
      test: /\.css$/,
      loader: "style!css"
    }
  ]
}

好きな書き方で書くことが出来ます。

  • "style!css!less"
  • "style!css!sass"
  • "style!css!stylus"

ref: http://webpack.github.io/docs/list-of-loaders.html#styling

プロジェクト中での使用法

こんな風に

ファイル構造
- component1/
-- index.js
-- template.html
-- style.css 
index.js
require('./style.css') // デフォルトだとrequireした時点で適用される

使用することで、部品要素に必要なスタイルを、必要な時だけ適用することができます。
styleを部品ごとに疎結合化することになるので、親要素のコンテキストを気にしないように記述が冗長になったりしますが、「スタイルを一つ変えると他の部分も影響を受けそうで怖い」ということも少なくなるので、記述者の精神が保たれることでしょう。

どうやって適用されるの

デフォルトだとrequireした時点で、<header>の最後尾に<style>として追加されます。

style/useableで適用タイミングを制御できる

ref: https://github.com/webpack/style-loader#reference-counted-api

そのままだとrequireした瞬間に適用されるstyle-loaderですが、
style/useableを使うことで、意図したタイミングで適用/除去を利用できるようになります。

// webpack.config.jsで "style/useable!css" 設定済みの場合
style = require('./style.css');
style.use();

SPAなどで、そのページ中では使わないが待機させてある要素のstylesheetなど、これがあるとかなり便利になります。

よいこと

css-loaderを使ってみて、よいなーと思ったこと一覧です。

リクエスト数が減る

<link>でstylesheetを読み込むのもリクエストのうちです。
それがJavascriptの中に内包できるので、img-loaderとかと併用すると究極的にはSPAのViewファイル構造がこうなります。

- index.html
- script.js

すごいさっぱり。
画像をbase64にすると容量が33.3%増えるので現実的にはおすすめ出来ないのですが、
出力するファイル構造の悩みをコードで解決出来るのは精神が洗われる気分。

stylesheetのゴミが出にくい

何度もデザインを変えていると、「これどのタイミングで使われるんだっけ...」みたいなゴミが出てくることもあるでしょう。
モジュールごとにrequireするスタイルだと、疎結合な書き方をせざるを得ないので、部品要素で使われることが保証されていると言えます。
後述するCSS Moduleの仕組みを使ったりすればスコープの悩みも消えますし、その時書く要素のことだけ考えれば良いという開発方法は更に精神が洗われる感じ。

よくないこと

Javascriptのサイズがヤバい

まだLTEが浸透しているとは言えないこんな世の中なので、モバイル環境だと無闇に1つのファイルに纏めた巨大Javascriptは致命傷だったりします。

  • Javascript, HTML, stylesheetが別ファイル
    • おー読み込んでる読み込んでる -> 表示された
  • 全部Javascript
    • 何も表示されねぇ... -> 表示された

完全に表示されるまでの時間が同じでも、この二つはかなり体験が違います。
回線の細さを気にする場面では、Javascrptを読み込むまでの間に何らかのごまかし方を考えるのが良いでしょう。

おまけ:css-loader

style-loaderの話とはずれますが、大抵の場合組み合わせるcss-loderの便利な機能も紹介します。

CSS Modules specを使うとスコープとか要らなくなる

CSS Moduleをご存知でしょうか?
CSSをモジュールに特化した書き方が出来るようにするプリプロセッサで、css-loaderでは簡単に恩恵に授かることができます。
css?modulesのように設定することで、:local:globalなどのセレクタが使えるようになり、要素間のCSSの衝突を防ぐことができます。

これが
:local(.className) {
  background: red;
  color: yellow;
}
こんなんになる
._23_aKvs-b8bW2Vg3fwHozO {
  background: red;
  color: yellow;
}

ユニークな親要素のスコープで囲んだり、prefixを付けるような作業とはお別れできるのですが、この方法を使うと人間には判別困難なclass-nameが付くことになるので、デバッグにはsourcemap必須ですね。

まとめ

今回はstyle-loaderを紹介しましたが、webpackではありとあらゆる拡張子がrequire出来ます。

ref: http://webpack.github.io/docs/list-of-loaders.html

バイナリの場合はコードになることでデメリットもあるのですが、通常文字列で表せるものは纏めてしまった方が幸せになることが多いのではないでしょうか。
特に出力先のフォルダ構造を気にかけなくて良くなるのは強力です。
どんどんリクエスト数減らしていきましょう。

nekobato
mercari
フリマアプリ「メルカリ」を、グローバルで開発しています。
https://tech.mercari.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away