KDE BLOG

バイブス

【webpack速習】vol.2: Asset Management

今回は下記ページの要点をまとめていきたいと思います。

webpack.js.org

  • webpackが登場する前は、Grunt や Gulp などを使ってJavaScript ファイル以外の画像のような他のアセットを処理して、/src フォルダから /dist または /build ディレクトリに移動していた。
  • 同じ考え方がJavaScriptモジュールにも使用されたが webpack のようなツールはすべての依存関係を動的に束ねる(依存関係グラフを作成する)
  • webpackの優れた機能の1つが、JavaScript以外に、他のどんな種類のファイルも含めることができる
    • JavaScriptモジュールのように明示的な依存関係の解決が、他のファイルにも適用できる

CSSを読み込む

JavaScriptモジュール内からCSSファイルをインポートするには、style-loadercss-loader をインストールして、webpack.config.js のモジュール設定に追加する。

yarn add style-loader css-loader --dev

▼ webpack.config.js

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  // ↓↓↓ ここから追加 ↓↓↓
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  }
};

test プロパティに書かれた正規表現を使ってどのファイルを探し、特定の Loader を提供すべきかを決定する。
この場合だと、.css で終わるファイルはすべて style-loader と css -loader に提供されて処理される。

▼ src/style.css

.hello {
  color: red;
}

▼ src/index.js

import _ from 'lodash';
import './style.css'; // 追加

function component() {
  let element = document.createElement('div');
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');
  element.classList.add('hello'); // 追加
  return element;
}

document.body.appendChild(component());

これで yarn build を実行してブラウザで index.html を見て Hello webpack が赤字になっていればOK。

補足

  • css-loader を使わないと、JavaScriptファイル内で CSSを import できない(ビルド時に import './style.css'; がエラーになる)
  • style-loader を使わないと、スタイルが適用されない。
    • style-loader を使うことで、import './style.css'; の内容が、head 要素内に <style> タグとして出力される

画像の読み込み

file-loader をインストールする

yarn add file-loader --dev

▼ webpack.config.js

  // 略
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
        ]
      },
      // ↓↓↓ 追加 ↓↓↓
      {
        test: /\.(png|svg|jpg|gif)/,
        use: [
          'file-loader',
        ]
      },
    ]
  },

これにより、index.js に依存する 画像ファイルが処理できるようになる。

▼ src/index.js

// 略
import Icon from './icon.png';

function component() {
  // 略
  const myIcon = new Image();
  myIcon.src = Icon;
  element.appendChild(myIcon);
  return element;
}

▼ src/style.css

.hello {
  color: red;
  background: url('./icon.png'); /* 追加 */
}

yarn build を実行すると、/src 内の icon.png が、ランダムな文字列のファイル名(自分の場合は ec8ccebd91480268a89ec15f8f2c43be.png)で /dist 内に出力される。
ブラウザで表示確認を行うと、appendされた画像、スタイルで指定された背景画像ともに、画像パスが生成されたランダム文字列のファイル名を参照するようになっている。

  • Iconを './icon.png' からインポートすると、その画像が処理されて出力ディレクトリに追加され、Icon 変数には処理後のその画像の最終的なURLが含まれる。
  • css-loader を使用すると、CSS内のURL( './icon.png' )に対しても同様の処理が行われる。
    • Loader はこれがローカルファイルであることを認識し、 './ icon.png' パスを出力ディレクトリ内の画像への最終パスに置き換える。
  • 画像を縮小し最適化するためにどうやればいいかは、image-webpack-loaderurl-loaderをチェック

フォントファイルを読み込む

フォントも基本、画像と同じく file-loader で処理する

▼ webpack.config.js

  // 略
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
        ]
      },
      {
        test: /\.(png|svg|jpg|gif)/,
        use: [
          'file-loader',
        ]
      },
      // ↓↓↓ 追加 ↓↓↓
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: [
          'file-loader',
        ]
      },
    ]
  },

フォントの扱いについて詳しい参考記事: survivejs.com

csvxmlなどのデータファイルを読み込む

JSON は標準でサポートされているため、デフォルトでインポートできる。
csv、tsv、xmlをインポートするには csv-loader xml-loader を使う。

yarn add csv-loader xml-loader --dev

▼ webpack.config.js

  // 略
  module: {
    rules: [
      // 略
      // ↓↓↓ 追加 ↓↓↓
      {
        test: /\.(csv|tsv)$/,
        use: [
          'csv-loader',
        ]
      },
      {
        test: /\.xml$/,
        use: [
          'xml-loader',
        ]
      },
    ]
  },

xmljsonファイルを作ってインポートする。

▼ src/data.xml

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>Mary</to>
  <from>John</from>
  <heading>Reminder</heading>
  <body>Call Cindy on Tuesday</body>
</note>

▼ src/data.json

[
  {
    "id": 1,
    "name": "Taro",
    "age": 20
  },
  {
    "id": 2,
    "name": "Bob",
    "age": 35
  }
]

▼ src/index.js

// 略
import xmlData from './data.xml';
import jsonData from './data.json';

function component() {
  // 略
  console.log('xmlData :', xmlData);
  console.log('jsonData :', jsonData);

  // 略  
}

yarn build 実行してブラウザのconsoleで確認できればOK。

※これは D3.js のようなデータ視覚化を実装するときに特に役に立つとのこと。
ページ読み込み時にajaxで取得する代わりにビルドプロセス中にロードしておけばすぐに使える。

グローバルなアセット

モジュールとアセットをグループ化することで、そのひとまとめをそのまま他の所で流用できるようになる。
たとえば下記の構造の場合、/my-components で使われるアセットはすべて同ディレクトリ内にあるので、移植性が高い

/components
|– /my-component
|  |– index.jsx
|  |– index.css
|  |– icon.svg
|  |– img.png

この構成が難しい場合は、アセットをひとつのベースディレクトリに格納してエイリアスを設定してインポートする。