アセットモジュールを使用すると、追加のローダーを構成することなく、アセットファイル(フォント、アイコンなど)を使用できます。
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.jsでentryフィールドを次のように更新します。
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ファイルは出力ディレクトリに出力され、そのパスはバンドルに注入されます。さらに、outputPath と publicPath をカスタマイズできます。
デフォルトでは、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.filenameは output.assetModuleFilename と同じで、assetとasset/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としてバンドルに注入されます。
デフォルトでは、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ファイルは、そのままの状態でバンドルに注入されます。
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の時点では、データURLもnew 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はデフォルトの条件に従って、resourceとinlineを自動的に選択するようになります。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-loaderをasset/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.generatorのemitオプションで実現できます。
module.exports = {
// …
module: {
rules: [
{
test: /\.png$/i,
type: 'asset/resource',
generator: {
emit: false,
},
},
],
},
};