アセット管理

最初からガイドに従っている場合は、「Hello webpack」と表示される小さなプロジェクトが完成しているはずです。では、画像などの他のアセットを組み込んで、どのように処理されるかを見てみましょう。

webpack以前は、フロントエンド開発者はgruntgulpなどのツールを使用して、これらのアセットを処理し、/srcフォルダから/distまたは/buildディレクトリに移動していました。JavaScriptモジュールにも同じ考え方が使用されていましたが、webpackなどのツールはすべての依存関係を**動的にバンドル**します(依存関係グラフと呼ばれるものを作成します)。これは、すべてのモジュールが**明示的に依存関係を宣言**するようになり、使用されていないモジュールをバンドルしなくなるため、素晴らしいことです。

webpackの最もクールな機能の1つは、ローダーまたは組み込みのアセットモジュールのサポートがあれば、JavaScript以外にも**あらゆる種類のファイルを含めることができる**ことです。つまり、JavaScriptについて上記で挙げた利点(明示的な依存関係など)は、WebサイトまたはWebアプリの構築に使用されるすべてに適用できます。すでに設定に精通している可能性のあるCSSから始めましょう。

設定

始める前に、プロジェクトに少し変更を加えましょう。

dist/index.html

 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8" />
-    <title>Getting Started</title>
+    <title>Asset Management</title>
   </head>
   <body>
-    <script src="main.js"></script>
+    <script src="bundle.js"></script>
   </body>
 </html>

webpack.config.js

 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
-    filename: 'main.js',
+    filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
 };

CSSの読み込み

JavaScriptモジュール内からCSSファイルをimportするには、style-loadercss-loaderをインストールしてmodule設定に追加する必要があります。

npm install --save-dev style-loader css-loader

webpack.config.js

 const path = require('path');

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

モジュールローダーはチェーン化できます。チェーン内の各ローダーは、処理されたリソースに変換を適用します。チェーンは逆順に実行されます。最初のローダーは、その結果(変換が適用されたリソース)を次のローダーに渡し、以降も同様です。最終的に、webpackはチェーンの最後のローダーによってJavaScriptが返されることを期待します。

上記のローダーの順序は維持する必要があります。'style-loader'が最初に来て、'css-loader'が続きます。この規則に従わないと、webpackはエラーをスローする可能性があります。

これにより、そのスタイリングに依存するファイルにimport './style.css'を記述できます。これで、そのモジュールが実行されると、文字列化されたCSSを含む<style>タグがhtmlファイルの<head>に挿入されます。

プロジェクトに新しいstyle.cssファイルを追加し、index.jsにインポートして試してみましょう。

プロジェクト

  webpack-demo
  |- package.json
  |- package-lock.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- style.css
    |- index.js
  |- /node_modules

src/style.css

.hello {
  color: red;
}

src/index.js

 import _ from 'lodash';
+import './style.css';

 function component() {
   const element = document.createElement('div');

   // Lodash, now imported by this script
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+  element.classList.add('hello');

   return element;
 }

 document.body.appendChild(component());

ビルドコマンドを実行します。

$ npm run build

...
[webpack-cli] Compilation finished
asset bundle.js 72.6 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1000 bytes 5 modules
orphan modules 326 bytes [orphan] 1 module
cacheable modules 539 KiB
  modules by path ./node_modules/ 538 KiB
    ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
    ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
    ./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
  modules by path ./src/ 965 bytes
    ./src/index.js + 1 modules 639 bytes [built] [code generated]
    ./node_modules/css-loader/dist/cjs.js!./src/style.css 326 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 2231 ms

ブラウザで再びdist/index.htmlを開くと、Hello webpackが赤色でスタイルされていることがわかります。webpack が何をしたかを確認するには、ページを検査します(ページソースを表示しても、<style>タグは JavaScript によって動的に作成されるため、結果は表示されません)。ページの head タグを見てください。 index.js でインポートしたスタイルブロックが含まれているはずです。

本番環境では、より高速な読み込み時間を実現するために、CSS を最小化することができます。ほとんどの場合、そうするべきです。その上で、思いつく限りのほぼすべての種類の CSS に対応するローダーが存在します。たとえば、postcsssasslessなどです。

画像の読み込み

これで CSS を取り込むことができましたが、背景やアイコンなどの画像はどうでしょうか? webpack 5 以降では、組み込みのアセットモジュールを使用して、それらをシステムに簡単に組み込むことができます。

webpack.config.js

 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
   module: {
     rules: [
       {
         test: /\.css$/i,
         use: ['style-loader', 'css-loader'],
       },
+      {
+        test: /\.(png|svg|jpg|jpeg|gif)$/i,
+        type: 'asset/resource',
+      },
     ],
   },
 };

import MyImage from './my-image.png'を実行すると、その画像は処理されてoutputディレクトリに追加され、MyImage変数には処理後の画像の最終 URL が含まれます。上記のようにcss-loaderを使用する場合、CSS 内のurl('./my-image.png')に対して同様の処理が行われます。ローダーはこれがローカルファイルであることを認識し、'./my-image.png'パスをoutputディレクトリ内の画像への最終パスに置き換えます。html-loaderは、<img src="./my-image.png" />を同じ方法で処理します。

プロジェクトに画像を追加して、これがどのように機能するかを見てみましょう。好きな画像を使用できます。

プロジェクト

  webpack-demo
  |- package.json
  |- package-lock.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- icon.png
    |- style.css
    |- index.js
  |- /node_modules

src/index.js

 import _ from 'lodash';
 import './style.css';
+import Icon from './icon.png';

 function component() {
   const element = document.createElement('div');

   // Lodash, now imported by this script
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
   element.classList.add('hello');

+  // Add the image to our existing div.
+  const myIcon = new Image();
+  myIcon.src = Icon;
+
+  element.appendChild(myIcon);
+
   return element;
 }

 document.body.appendChild(component());

src/style.css

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

新しいビルドを作成し、index.htmlファイルを再び開いてみましょう。

$ npm run build

...
[webpack-cli] Compilation finished
assets by status 9.88 KiB [cached] 1 asset
asset bundle.js 73.4 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1.82 KiB 6 modules
orphan modules 326 bytes [orphan] 1 module
cacheable modules 540 KiB (javascript) 9.88 KiB (asset)
  modules by path ./node_modules/ 539 KiB
    modules by path ./node_modules/css-loader/dist/runtime/*.js 2.38 KiB
      ./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
      ./node_modules/css-loader/dist/runtime/getUrl.js 830 bytes [built] [code generated]
    ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
    ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
  modules by path ./src/ 1.45 KiB (javascript) 9.88 KiB (asset)
    ./src/index.js + 1 modules 794 bytes [built] [code generated]
    ./src/icon.png 42 bytes (javascript) 9.88 KiB (asset) [built] [code generated]
    ./node_modules/css-loader/dist/cjs.js!./src/style.css 648 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 1972 ms

すべてがうまくいけば、アイコンが繰り返し背景として、またHello webpackテキストの横にimg要素として表示されるはずです。この要素を検査すると、実際のファイル名が29822eaa871e8eadeaa4.pngのようなものに変更されていることがわかります。これは、webpack がsrcフォルダ内のファイルを見つけて処理したことを意味します!

フォントの読み込み

それでは、フォントなどの他のアセットはどうでしょうか?アセットモジュールは、それらを介してロードするすべてのファイルを受け取り、ビルドディレクトリに出力します。これは、フォントを含むあらゆる種類のファイルに使用できることを意味します。フォントファイルを処理するようにwebpack.config.jsを更新してみましょう。

webpack.config.js

 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
   module: {
     rules: [
       {
         test: /\.css$/i,
         use: ['style-loader', 'css-loader'],
       },
       {
         test: /\.(png|svg|jpg|jpeg|gif)$/i,
         type: 'asset/resource',
       },
+      {
+        test: /\.(woff|woff2|eot|ttf|otf)$/i,
+        type: 'asset/resource',
+      },
     ],
   },
 };

プロジェクトにフォントファイルをいくつか追加します。

プロジェクト

  webpack-demo
  |- package.json
  |- package-lock.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- my-font.woff
+   |- my-font.woff2
    |- icon.png
    |- style.css
    |- index.js
  |- /node_modules

ローダーが設定され、フォントが配置されたら、@font-face宣言を介してそれらを組み込むことができます。画像の場合と同様に、ローカルのurl(...)ディレクティブは webpack によって取得されます。

src/style.css

+@font-face {
+  font-family: 'MyFont';
+  src: url('./my-font.woff2') format('woff2'),
+    url('./my-font.woff') format('woff');
+  font-weight: 600;
+  font-style: normal;
+}
+
 .hello {
   color: red;
+  font-family: 'MyFont';
   background: url('./icon.png');
 }

新しいビルドを実行して、webpack がフォントを処理したかどうかを確認しましょう。

$ npm run build

...
[webpack-cli] Compilation finished
assets by status 9.88 KiB [cached] 1 asset
assets by info 33.2 KiB [immutable]
  asset 55055dbfc7c6a83f60ba.woff 18.8 KiB [emitted] [immutable] [from: src/my-font.woff] (auxiliary name: main)
  asset 8f717b802eaab4d7fb94.woff2 14.5 KiB [emitted] [immutable] [from: src/my-font.woff2] (auxiliary name: main)
asset bundle.js 73.7 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1.82 KiB 6 modules
orphan modules 326 bytes [orphan] 1 module
cacheable modules 541 KiB (javascript) 43.1 KiB (asset)
  javascript modules 541 KiB
    modules by path ./node_modules/ 539 KiB
      modules by path ./node_modules/css-loader/dist/runtime/*.js 2.38 KiB 2 modules
      ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
      ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
    modules by path ./src/ 1.98 KiB
      ./src/index.js + 1 modules 794 bytes [built] [code generated]
      ./node_modules/css-loader/dist/cjs.js!./src/style.css 1.21 KiB [built] [code generated]
  asset modules 126 bytes (javascript) 43.1 KiB (asset)
    ./src/icon.png 42 bytes (javascript) 9.88 KiB (asset) [built] [code generated]
    ./src/my-font.woff2 42 bytes (javascript) 14.5 KiB (asset) [built] [code generated]
    ./src/my-font.woff 42 bytes (javascript) 18.8 KiB (asset) [built] [code generated]
webpack 5.4.0 compiled successfully in 2142 ms

dist/index.htmlを再び開き、Hello webpackテキストが新しいフォントに変更されているかどうかを確認します。すべてうまくいけば、変更が表示されるはずです。

データの読み込み

ロードできるもう1つの便利なアセットは、JSONファイル、CSV、TSV、XMLなどのデータです。JSONのサポートは、NodeJSと同様に実際に組み込まれています。つまり、import Data from './data.json'はデフォルトで機能します。CSV、TSV、XMLをインポートするには、csv-loaderxml-loaderを使用できます。3つすべての読み込みを処理してみましょう。

npm install --save-dev csv-loader xml-loader

webpack.config.js

 const path = require('path');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
   module: {
     rules: [
       {
         test: /\.css$/i,
         use: ['style-loader', 'css-loader'],
       },
       {
         test: /\.(png|svg|jpg|jpeg|gif)$/i,
         type: 'asset/resource',
       },
       {
         test: /\.(woff|woff2|eot|ttf|otf)$/i,
         type: 'asset/resource',
       },
+      {
+        test: /\.(csv|tsv)$/i,
+        use: ['csv-loader'],
+      },
+      {
+        test: /\.xml$/i,
+        use: ['xml-loader'],
+      },
     ],
   },
 };

プロジェクトにデータファイルをいくつか追加します。

プロジェクト

  webpack-demo
  |- package.json
  |- package-lock.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
+   |- data.xml
+   |- data.csv
    |- my-font.woff
    |- my-font.woff2
    |- icon.png
    |- style.css
    |- index.js
  |- /node_modules

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.csv

to,from,heading,body
Mary,John,Reminder,Call Cindy on Tuesday
Zoe,Bill,Reminder,Buy orange juice
Autumn,Lindsey,Letter,I miss you

これで、4種類のデータ(JSON、CSV、TSV、XML)のいずれかをimportできます。インポートするData変数には、消費用に解析されたJSONが含まれます。

src/index.js

 import _ from 'lodash';
 import './style.css';
 import Icon from './icon.png';
+import Data from './data.xml';
+import Notes from './data.csv';

 function component() {
   const element = document.createElement('div');

   // Lodash, now imported by this script
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
   element.classList.add('hello');

   // Add the image to our existing div.
   const myIcon = new Image();
   myIcon.src = Icon;

   element.appendChild(myIcon);

+  console.log(Data);
+  console.log(Notes);
+
   return element;
 }

 document.body.appendChild(component());

npm run buildコマンドを再実行し、dist/index.htmlを開きます。開発者ツールのコンソールを見ると、インポートされたデータがコンソールにログされていることがわかります。

// No warning
import data from './data.json';

// Warning shown, this is not allowed by the spec.
import { foo } from './data.json';

JSONモジュールのパーサーのカスタマイズ

特定のwebpackローダーの代わりにカスタムパーサーを使用することにより、tomlyaml、またはjson5ファイルをJSONモジュールとしてインポートできます。

srcフォルダの下にdata.tomldata.yaml、およびdata.json5ファイルがあるとします。

src/data.toml

title = "TOML Example"

[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z

src/data.yaml

title: YAML Example
owner:
  name: Tom Preston-Werner
  organization: GitHub
  bio: |-
    GitHub Cofounder & CEO
    Likes tater tots and beer.
  dob: 1979-05-27T07:32:00.000Z

src/data.json5

{
  // comment
  title: 'JSON5 Example',
  owner: {
    name: 'Tom Preston-Werner',
    organization: 'GitHub',
    bio: 'GitHub Cofounder & CEO\n\
Likes tater tots and beer.',
    dob: '1979-05-27T07:32:00.000Z',
  },
}

最初にtomlyamljs、およびjson5パッケージをインストールします。

npm install toml yamljs json5 --save-dev

そして、webpack設定でそれらを設定します。

webpack.config.js

 const path = require('path');
+const toml = require('toml');
+const yaml = require('yamljs');
+const json5 = require('json5');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
   module: {
     rules: [
       {
         test: /\.css$/i,
         use: ['style-loader', 'css-loader'],
       },
       {
         test: /\.(png|svg|jpg|jpeg|gif)$/i,
         type: 'asset/resource',
       },
       {
         test: /\.(woff|woff2|eot|ttf|otf)$/i,
         type: 'asset/resource',
       },
       {
         test: /\.(csv|tsv)$/i,
         use: ['csv-loader'],
       },
       {
         test: /\.xml$/i,
         use: ['xml-loader'],
       },
+      {
+        test: /\.toml$/i,
+        type: 'json',
+        parser: {
+          parse: toml.parse,
+        },
+      },
+      {
+        test: /\.yaml$/i,
+        type: 'json',
+        parser: {
+          parse: yaml.parse,
+        },
+      },
+      {
+        test: /\.json5$/i,
+        type: 'json',
+        parser: {
+          parse: json5.parse,
+        },
+      },
     ],
   },
 };

src/index.js

 import _ from 'lodash';
 import './style.css';
 import Icon from './icon.png';
 import Data from './data.xml';
 import Notes from './data.csv';
+import toml from './data.toml';
+import yaml from './data.yaml';
+import json from './data.json5';
+
+console.log(toml.title); // output `TOML Example`
+console.log(toml.owner.name); // output `Tom Preston-Werner`
+
+console.log(yaml.title); // output `YAML Example`
+console.log(yaml.owner.name); // output `Tom Preston-Werner`
+
+console.log(json.title); // output `JSON5 Example`
+console.log(json.owner.name); // output `Tom Preston-Werner`

 function component() {
   const element = document.createElement('div');

   // Lodash, now imported by this script
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
   element.classList.add('hello');

   // Add the image to our existing div.
   const myIcon = new Image();
   myIcon.src = Icon;

   element.appendChild(myIcon);

   console.log(Data);
   console.log(Notes);

   return element;
 }

 document.body.appendChild(component());

npm run buildコマンドを再実行し、dist/index.htmlを開きます。インポートされたデータがコンソールに記録されていることがわかります。

グローバルアセット

上記で述べたすべてのことの最もクールな部分は、このようにアセットを読み込むことで、モジュールとアセットをより直感的な方法でグループ化できることです。すべてを含むグローバルな/assetsディレクトリに依存する代わりに、アセットを使用するコードでアセットをグループ化できます。たとえば、このような構造が役立ちます。

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

この設定により、密接に結合されたすべてが一緒に存在するため、コードの移植性が大幅に向上します。たとえば、/my-componentを別のプロジェクトで使用する場合、それをそこの/componentsディレクトリにコピーまたは移動します。_外部依存関係_がインストールされていて、_設定で同じローダーが定義されていれば_、準備完了です。

ただし、古い方法に縛られている場合や、複数のコンポーネント(ビュー、テンプレート、モジュールなど)で共有されるアセットがある場合は、これらのアセットをベースディレクトリに保存し、エイリアシングを使用してimportしやすくすることもできます。

まとめ

次のガイドでは、このガイドで使用したすべてのアセットを使用するわけではないため、ガイドの次の部分である出力管理に備えてクリーンアップを行いましょう。

プロジェクト

  webpack-demo
  |- package.json
  |- package-lock.json
  |- webpack.config.js
  |- /dist
    |- bundle.js
    |- index.html
  |- /src
-   |- data.csv
-   |- data.json5
-   |- data.toml
-   |- data.xml
-   |- data.yaml
-   |- icon.png
-   |- my-font.woff
-   |- my-font.woff2
-   |- style.css
    |- index.js
  |- /node_modules

webpack.config.js

 const path = require('path');
-const toml = require('toml');
-const yaml = require('yamljs');
-const json5 = require('json5');

 module.exports = {
   entry: './src/index.js',
   output: {
     filename: 'bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
-  module: {
-    rules: [
-      {
-        test: /\.css$/i,
-        use: ['style-loader', 'css-loader'],
-      },
-      {
-        test: /\.(png|svg|jpg|jpeg|gif)$/i,
-        type: 'asset/resource',
-      },
-      {
-        test: /\.(woff|woff2|eot|ttf|otf)$/i,
-        type: 'asset/resource',
-      },
-      {
-        test: /\.(csv|tsv)$/i,
-        use: ['csv-loader'],
-      },
-      {
-        test: /\.xml$/i,
-        use: ['xml-loader'],
-      },
-      {
-        test: /\.toml$/i,
-        type: 'json',
-        parser: {
-          parse: toml.parse,
-        },
-      },
-      {
-        test: /\.yaml$/i,
-        type: 'json',
-        parser: {
-          parse: yaml.parse,
-        },
-      },
-      {
-        test: /\.json5$/i,
-        type: 'json',
-        parser: {
-          parse: json5.parse,
-        },
-      },
-    ],
-  },
 };

src/index.js

 import _ from 'lodash';
-import './style.css';
-import Icon from './icon.png';
-import Data from './data.xml';
-import Notes from './data.csv';
-import toml from './data.toml';
-import yaml from './data.yaml';
-import json from './data.json5';
-
-console.log(toml.title); // output `TOML Example`
-console.log(toml.owner.name); // output `Tom Preston-Werner`
-
-console.log(yaml.title); // output `YAML Example`
-console.log(yaml.owner.name); // output `Tom Preston-Werner`
-
-console.log(json.title); // output `JSON5 Example`
-console.log(json.owner.name); // output `Tom Preston-Werner`

 function component() {
   const element = document.createElement('div');

-  // Lodash, now imported by this script
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');
-  element.classList.add('hello');
-
-  // Add the image to our existing div.
-  const myIcon = new Image();
-  myIcon.src = Icon;
-
-  element.appendChild(myIcon);
-
-  console.log(Data);
-  console.log(Notes);

   return element;
 }

 document.body.appendChild(component());

そして、前に追加した依存関係を削除します。

npm uninstall css-loader csv-loader json5 style-loader toml xml-loader yamljs

次のガイド

出力管理に進みましょう。

さらに読む

10 貢献者

skipjackmichael-ciniawskyTheDutchCodersudarsangpchenxsanEugeneHlushkoAnayaDesignwizardofhogwartsastonizersnitin315