アセットモジュール

アセットモジュールを使用すると、追加のローダーを構成することなく、アセットファイル(フォント、アイコンなど)を使用できます。

webpack 5より前は、一般的に使用されていました。

  • raw-loaderは、ファイルを文字列としてインポートします
  • url-loaderは、ファイルをデータURIとしてバンドルにインライン化します
  • file-loaderは、ファイルを出力ディレクトリに排出します

アセットモジュールタイプは、4つの新しいモジュールタイプを追加することで、これらのすべてのローダーを置き換えます

  • asset/resourceは、別のファイルを排出し、URLをエクスポートします。以前は、file-loaderを使用することで実現できました。
  • asset/inlineは、アセットのデータURIをエクスポートします。以前は、url-loaderを使用することで実現できました。
  • asset/sourceは、アセットのソースコードをエクスポートします。以前は、raw-loaderを使用することで実現できました。
  • assetは、データURIのエクスポートと別のファイルの排出の間で自動的に選択します。以前は、アセットサイズ制限付きのurl-loaderを使用することで実現できました。

webpack 5で、古いアセットローダー(つまり、file-loader/url-loader/raw-loader)をアセットモジュールと一緒に使用すると、アセットの重複につながるため、アセットモジュールがアセットを再び処理しないようにする必要があります。これは、アセットのモジュールタイプを'javascript/auto'に設定することで実行できます。

webpack.config.js

module.exports = {
  module: {
   rules: [
      {
        test: /\.(png|jpg|gif)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            }
          },
        ],
+       type: 'javascript/auto'
      },
   ]
  },
}

新しいURL呼び出しから発生したアセットをアセットローダーから除外するには、ローダー構成にdependency: { not: ['url'] }を追加します。

webpack.config.js

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|gif)$/i,
+       dependency: { not: ['url'] },
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
    ],
  }
}

パブリックパス

デフォルトでは、内部的には、assetタイプは__webpack_public_path__ + import.metaを実行します。つまり、構成でoutput.publicPathを設定すると、assetがロードされるURLをオーバーライドできます。

オンザフライ上書き

コードで__webpack_public_path__を設定する場合、assetのロードロジックを壊さないように達成する必要がある方法は、アプリの最初のコードとして実行し、関数を使用しないようにすることです。この例として、publicPath.jsというファイルがあり、コンテンツは次のようになります。

__webpack_public_path__ = 'https://cdn.url.com';

次に、webpack.config.jsentryフィールドを次のように更新します。

module.exports = {
  entry: ['./publicPath.js', './App.js'],
};

あるいは、webpackの設定を変更せずに、App.js内で以下のように記述することもできます。唯一の欠点は、ここで順序を強制する必要があり、一部のリンティングツールと衝突する可能性があることです。

import './publicPath.js';

リソースアセット

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
+ module: {
+   rules: [
+     {
+       test: /\.png/,
+       type: 'asset/resource'
+     }
+   ]
+ },
};

src/index.js

import mainImage from './images/main.png';

img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'

すべての.pngファイルは出力ディレクトリに出力され、そのパスはバンドルに注入されます。さらに、outputPathpublicPath をカスタマイズできます。

カスタム出力ファイル名

デフォルトでは、asset/resourceモジュールは、[hash][ext][query]というファイル名で出力ディレクトリに出力されます。

このテンプレートは、webpack設定で output.assetModuleFilename を設定することで変更できます。

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
+   assetModuleFilename: 'images/[hash][ext][query]'
  },
  module: {
    rules: [
      {
        test: /\.png/,
        type: 'asset/resource'
      }
    ]
  },
};

出力ファイル名をカスタマイズするもう一つのケースは、特定の種類のアセットを指定されたディレクトリに出力することです。

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
+   assetModuleFilename: 'images/[hash][ext][query]'
  },
  module: {
    rules: [
      {
        test: /\.png/,
        type: 'asset/resource'
-     }
+     },
+     {
+       test: /\.html/,
+       type: 'asset/resource',
+       generator: {
+         filename: 'static/[hash][ext][query]'
+       }
+     }
    ]
  },
};

この設定では、すべてのhtmlファイルが出力ディレクトリ内のstaticディレクトリに出力されます。

Rule.generator.filenameoutput.assetModuleFilename と同じで、assetasset/resourceのモジュールタイプでのみ機能します。

アセットのインライン化

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
-   assetModuleFilename: 'images/[hash][ext][query]'
  },
  module: {
    rules: [
      {
-       test: /\.png/,
-       type: 'asset/resource'
+       test: /\.svg/,
+       type: 'asset/inline'
-     },
+     }
-     {
-       test: /\.html/,
-       type: 'asset/resource',
-       generator: {
-         filename: 'static/[hash][ext][query]'
-       }
-     }
    ]
  }
};

src/index.js

- import mainImage from './images/main.png';
+ import metroMap from './images/metro.svg';

- img.src = mainImage; // '/dist/151cfcfa1bd74779aadb.png'
+ block.style.background = `url(${metroMap})`; // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)

すべての.svgファイルは、データURIとしてバンドルに注入されます。

カスタムデータURIジェネレーター

デフォルトでは、webpackによって出力されるデータURIは、Base64アルゴリズムを使用してエンコードされたファイルの内容を表します。

カスタムエンコードアルゴリズムを使用したい場合は、ファイルの内容をエンコードするカスタム関数を指定できます。

webpack.config.js

const path = require('path');
+ const svgToMiniDataURI = require('mini-svg-data-uri');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.svg/,
        type: 'asset/inline',
+       generator: {
+         dataUrl: content => {
+           content = content.toString();
+           return svgToMiniDataURI(content);
+         }
+       }
      }
    ]
  },
};

これで、すべての.svgファイルはmini-svg-data-uriパッケージによってエンコードされます。

ソースアセット

webpack.config.js

const path = require('path');
- const svgToMiniDataURI = require('mini-svg-data-uri');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
-       test: /\.svg/,
-       type: 'asset/inline',
-       generator: {
-         dataUrl: content => {
-           content = content.toString();
-           return svgToMiniDataURI(content);
-         }
-       }
+       test: /\.txt/,
+       type: 'asset/source',
      }
    ]
  },
};

src/example.txt

Hello world

src/index.js

- import metroMap from './images/metro.svg';
+ import exampleText from './example.txt';

- block.style.background = `url(${metroMap}); // url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDo...vc3ZnPgo=)
+ block.textContent = exampleText; // 'Hello world'

すべての.txtファイルは、そのままの状態でバンドルに注入されます。

URLアセット

new URL('./path/to/asset', import.meta.url)を使用すると、webpackもアセットモジュールを作成します。

src/index.js

const logo = new URL('./logo.svg', import.meta.url);

webpackは設定のtargetに応じて、上記のコードを異なる結果にコンパイルします。

// target: web
new URL(
  __webpack_public_path__ + 'logo.svg',
  document.baseURI || self.location.href
);

// target: webworker
new URL(__webpack_public_path__ + 'logo.svg', self.location);

// target: node, node-webkit, nwjs, electron-main, electron-renderer, electron-preload, async-node
new URL(
  __webpack_public_path__ + 'logo.svg',
  require('url').pathToFileUrl(__filename)
);

webpack 5.38.0の時点では、データURLnew URL()でサポートされています。

src/index.js

const url = new URL('data:,', import.meta.url);
console.log(url.href === 'data:,');
console.log(url.protocol === 'data:');
console.log(url.pathname === ',');

一般的なアセットタイプ

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
+       test: /\.txt/,
+       type: 'asset',
      }
    ]
  },
};

webpackはデフォルトの条件に従って、resourceinlineを自動的に選択するようになります。8kb未満のサイズのファイルはinlineモジュールタイプとして扱われ、それ以外の場合はresourceモジュールタイプとして扱われます。

この条件は、webpack設定のモジュールルールレベルでRule.parser.dataUrlCondition.maxSizeオプションを設定することで変更できます。

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.txt/,
        type: 'asset',
+       parser: {
+         dataUrlCondition: {
+           maxSize: 4 * 1024 // 4kb
+         }
+       }
      }
    ]
  },
};

また、モジュールをインライン化するかどうかを決定するために、関数を指定することもできます。

インラインローダー構文の置換

アセットモジュールとWebpack 5以前は、上記のレガシーローダーでインライン構文を使用することができました。

現在は、すべてのインラインローダー構文を削除し、インライン構文の機能を模倣するためにresourceQuery条件を使用することが推奨されています。

たとえば、raw-loaderasset/sourceタイプに置き換える場合

- import myModule from 'raw-loader!my-module';
+ import myModule from 'my-module?raw';

webpackの設定では

module: {
    rules: [
    // ...
+     {
+       resourceQuery: /raw/,
+       type: 'asset/source',
+     }
    ]
  },

他のローダーによる処理からrawアセットを除外したい場合は、否定条件を使用します。

module: {
    rules: [
    // ...
+     {
+       test: /\.m?js$/,
+       resourceQuery: { not: [/raw/] },
+       use: [ ... ]
+     },
      {
        resourceQuery: /raw/,
        type: 'asset/source',
      }
    ]
  },

または、ルールのoneOfリストを使用します。ここでは、最初に一致するルールのみが適用されます。

module: {
    rules: [
    // ...
+     { oneOf: [
        {
          resourceQuery: /raw/,
          type: 'asset/source',
        },
+       {
+         test: /\.m?js$/,
+         use: [ ... ]
+       },
+     ] }
    ]
  },

アセットの出力の無効化

サーバーサイドレンダリングのようなユースケースでは、アセットの出力を無効にしたい場合があります。これは、Rule.generatoremitオプションで実現できます。

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.png$/i,
        type: 'asset/resource',
        generator: {
          emit: false,
        },
      },
    ],
  },
};

6 貢献者

smelukovEugeneHlushkochenxsananshumanvspence-sdkdk225