资产模块

资产模块允许您使用资产文件(字体、图标等)而无需配置其他加载器。

在 webpack 5 之前,通常使用

资产模块类型通过添加 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'],
};

或者,您可以在您的 App.js 中执行以下操作,而无需修改您的 webpack 配置。唯一的缺点是您必须在这里强制排序,这可能会与某些 linting 工具冲突。

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);

根据你配置中的 target,webpack 会将上面的代码编译成不同的结果。

// 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 将根据默认条件自动在 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 条件来模拟内联语法的功能。

例如,在用 asset/source 类型替换 raw-loader 的情况下。

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

在 webpack 配置中。

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

如果你想排除原始资产被其他加载器处理,请使用否定条件。

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,
        },
      },
    ],
  },
};

6 位贡献者

smelukovEugeneHlushkochenxsananshumanvspence-sdkdk225