Reactとcss-loader

ReactとCSSを書いた後にRadiumを使ってみたのだけど、いまいち微妙な感じだった。 CSSをJSのオブジェクトで書いて、頑張ってタグのひとつひとつにstyle属性を書いていく、というふうなことになって普通にCSSを書くよりもかなり残念な感じになった。 (もしかすると自分のRadiumの使い方がおかしいのかもしれないのだけど)

css-loader

数日前たまたまcss-loaderのリポジトリを見ていたら、ローカルスコープ風にCSSが書ける、というような記述があって気になって試してみた。

webpack.config.coffee

webpack.config.jsからwebpack.config.coffeeを読み込むように書いて、webpack.config.coffeeに以下の様な設定を書く。

  module:
    loaders: [
      { test: /\.css$/, loader: 'style!css' }
      { test: /\.jsx$/, exclude: /node_modules|bower_components/, loader: 'babel?stage=0' }
    ]

必要なloaderは

  • style-loader
  • css-loader
  • babel-loader

になる。

index.jsx

ページで読み込むスクリプトを以下の様に書く。webpackの設定は適当に。

import React from 'react';
import Component from './component.jsx';

React.render(<Component/>, document.getElementById('component'));

component.jsx

index.jsxから読み込むコンポーネントのスクリプトを以下の様に書く。

import React from 'react';
import CSS from './component.css';

export default class Component extends React.Component {
  render() {
    return (
      <div className={CSS.component}>aaa</div>
    );
  }
};

component.css

component.jsxで使うスタイルシートを以下の様に書く。

:local .component {
    background-color: #000;
}

どうも、css-loaderはcss-modulesの仕様?を扱えるようで:localというのもその仕様の一部のようだ。 css-modulesはプリプロセッサでも扱えるらしく、Lessでのサンプルがあるので恐らくSass/Scssでも使えるのだと思う。

index.html

ページそのものを以下の様に書く。

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta charset="utf-8">
    <title>React and css-loader</title>
  </head>
  <body>
    <div id="component"></div>
    <script src="index.js"></script>
  </body>
</html>

これでページを表示させてみると以下の様なHTMLが出力される。

rendered index.html

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta charset="utf-8">
    <title>React and css-loader</title>
    <style type="text/css">
      ._1kTSctKto5gsg1BgHm2QbH {
        background-color: #000;
      }
    </style>
  </head>
  <body>
    <div id="component">
      <div class="_1kTSctKto5gsg1BgHm2QbH" data-reactid=".0">aaa</div>
    </div>
    <script src="index.js"></script>
  </body>
</html>

Reactで作ったコンポーネントが追加されていたり、謎のstyleタグが追加されていたり、謎のクラス名が出力されていたりすると思う。


Reactで作ったコンポーネントが追加されているのは、React.render()をしたからなので普通の挙動だと思う。

謎のstyleタグが追加されているのはstyle-loaderの仕業で、importもしくはrequireするとheadタグの最後にstyleタグが追加されるようになる。

謎のクラス名が出力されているのはcss-loaderの仕業で、:localが指定されたクラス名はハッシュ値の様な文字列のクラス名に変換される。 加えて、変換前のクラス名をキーに、変換後のクラス名を値に持ったオブジェクトをエクスポートするモジュールになる。

これでコンポーネントローカルなスタイルシートが適用できるようになったと思う。 クラス名がハッシュ値のような文字列なので、普通の人間が書いている分にはクラス名が重複したりするようなことは普通は無いと思う。


Reactを使わないで普通のウェブページを作るときにも応用できそうで、 その場合はタグにidやclassを付けておいてそれに対してcss-loaderで変換されたクラス名を付ける、 などをすればスタイルシートのモジュール化ができそうかな、と思った。

<p class="js-style"></p>
:local .paragraph {
  color: #999;
}
import CSS from './style.css';

$('.js-style').addClass(CSS.paragraph);

良さそうな気がする。難点としてはJavaScriptをOFFにされているとスタイルシートがあたらないところか。

JadeでHTMLを出力するとして静的にクラス名をタグに指定するとか、なんとかうまく解決できないものかな。