2025年3月15日 星期六 甲辰(龙)年 月十四 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > JavaScript

webpack进阶(五)

时间:02-20来源:作者:点击数:14

十七、资源模块

资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

在 webpack 5 之前,通常使用:

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

当在 webpack 5 中使用旧的 assets loader(如 file-loader/url-loader/raw-loader 等)和 asset 模块时,你可能想停止当前 asset 模块的处理,并再次启动处理,这可能会导致 asset 重复,你可以通过将 asset 模块的类型设置为 'javascript/auto' 来解决。

webpack.config.js

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

如需从 asset loader 中排除来自新 URL 处理的 asset,请添加 dependency: { not: ['url'] } 到 loader 配置中。

webpack.config.js

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

1、Resource 资源

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 文件都将被发送到输出目录,并且其路径将被注入到 bundle 中。

(1) 自定义输出文件名

默认情况下,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 模块类型。

2、inline 资源(inlining asset)

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 文件都将作为 data URI 注入到 bundle 中。

(1) 自定义 data URI 生成器

webpack 输出的 data 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 包进行编码。

3、source 资源(source asset)

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 文件将原样注入到 bundle 中。

4、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)
  • );

5、通用资源类型

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 配置的 module rule 层级中,设置 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
  • + }
  • + }
  • }
  • ]
  • },
  • };

还可以 指定一个函数 来决定是否 inline 模块。

6、变更内联 loader 的语法

在 asset 模块和 webpack 5 之前,可以使用内联语法与上述传统的 loader 结合使用。

现在建议去掉所有的 loader 的语法,使用资源查询条件来魔法内联语法的功能。

示例,将 raw-loader 替换为 asset/source 类型:

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

webpack 相关配置:

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

如果你想把原始资源排除在其他 loader 的解析范围以外,请使用取反的符合:

  • module: {
  • rules: [
  • // ...
  • + {
  • + test: /\.m?js$/,
  • + resourceQuery: /^(?!raw$).*/,
  • + },
  • {
  • resouceQuery: /raw/
  • type: 'asset/source'
  • }
  • ]
  • },

十八、entry 高级用法

1、每个入口使用多种文件类型

在不使用 import 样式文件的应用程序中(预单页应用程序或其他原因),使用一个值数组结构的 entry,并且在其中传入不同类型的文件,可以实现将 CSS 和 JavaScript(和其他)文件分离在不同的 bundle。

举个例子。我们有一个具有两种页面类型的 PHP 应用程序:home(首页) 和 account(帐户)。home 与应用程序其余部分(account 页面)具有不同的布局和不可共享的 JavaScript。我们想要从应用程序文件中输出 home 页面的 home.js 和 home.css,为 account 页面输出 account.js 和 account.css

home.js

  • console.log('home page type');

home.scss

  • // home page individual styles

account.js

  • console.log('account page type');

account.scss

  • // account page individual styles

我们将在 production(生产) 模式中使用 MiniCssExtractPlugin 作为 CSS 的一个最佳实践。

webpack.config.js

  • const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  • module.exports = {
  • mode: process.env.NODE_ENV,
  • entry: {
  • home: ['./home.js', './home.scss'],
  • account: ['./account.js', './account.scss'],
  • },
  • output: {
  • filename: '[name].js',
  • },
  • module: {
  • rules: [
  • {
  • test: /\.scss$/,
  • use: [
  • // fallback to style-loader in development
  • process.env.NODE_ENV !== 'production'
  • ? 'style-loader'
  • : MiniCssExtractPlugin.loader,
  • 'css-loader',
  • 'sass-loader',
  • ],
  • },
  • ],
  • },
  • plugins: [
  • new MiniCssExtractPlugin({
  • filename: '[name].css',
  • }),
  • ],
  • };

由于我们未指定其他输出路径,因此使用以上配置运行 webpack 将输出到 ./dist./dist 目录下现在包含四个文件:

  • home.js
  • home.css
  • account.js
  • account.css

十九、TypeScript

TypeScript 是 JavaScript 的超集,为其增加了类型系统,可以编译为普通 JavaScript 代码。这篇指南里我们将会学习是如何将 webpack 和 TypeScript 进行集成。

1、基础配置

首先,执行以下命令安装 TypeScript compiler 和 loader:

  • npm install --save-dev typescript ts-loader

现在,我们将修改目录结构和配置文件:

project

  • webpack-demo
  • |- package.json
  • + |- tsconfig.json
  • |- webpack.config.js
  • |- /dist
  • |- bundle.js
  • |- index.html
  • |- /src
  • |- index.js
  • + |- index.ts
  • |- /node_modules

tsconfig.json

这里我们设置一个基本的配置,来支持 JSX,并将 TypeScript 编译到 ES5……

  • {
  • "compilerOptions": {
  • "outDir": "./dist/",
  • "noImplicitAny": true,
  • "module": "es6",
  • "target": "es5",
  • "jsx": "react",
  • "allowJs": true
  • }
  • }

查看 TypeScript 官方文档 了解更多关于 tsconfig.json 的配置选项。

想要了解 webpack 配置的更多信息,请查看 配置 概念。

现在,配置 webpack 处理 TypeScript:

webpack.config.js

  • const path = require('path');
  • module.exports = {
  • entry: './src/index.ts',
  • module: {
  • rules: [
  • {
  • test: /\.tsx?$/,
  • use: 'ts-loader',
  • exclude: /node_modules/,
  • },
  • ],
  • },
  • resolve: {
  • extensions: ['.tsx', '.ts', '.js'],
  • },
  • output: {
  • filename: 'bundle.js',
  • path: path.resolve(__dirname, 'dist'),
  • },
  • };

这会让 webpack 直接从 ./index.ts 进入,然后通过 ts-loader _加载_所有的 .ts 和 .tsx 文件,并且在当前目录_输出_一个 bundle.js 文件。

现在让我们改变 lodash 在 ./index.ts 文件中的引入, 因为在 lodash 的定义中没有默认(default)的导出。

./index.ts

  • - import _ from 'lodash';
  • + import * as _ from 'lodash';
  • function component() {
  • const element = document.createElement('div');
  • element.innerHTML = _.join(['Hello', 'webpack'], ' ');
  • return element;
  • }
  • document.body.appendChild(component());

Tip

如果想在 TypeScript 中保留如import _ from 'lodash';的语法被让它作为一种默认的导入方式,需要在文件 tsconfig.json 中设置 "allowSyntheticDefaultImports" : true 和 "esModuleInterop" : true 。这个是与 TypeScript 相关的配置,在本文档中提及仅供参考。

2、Loader

ts-loader

在本指南中,我们使用 ts-loader,因为它能够很方便地启用额外的 webpack 功能,例如将其他 web 资源导入到项目中。

Warning

ts-loader使用tscTypeScript编译器,并取决于您的tsconfig.json配置。确保避免设置module为“ CommonJS”,否则webpack将无法摇晃您的代码

请注意,如果您已经在使用babel-loader代码转译,则可以使用@babel/preset-typescriptBabel并让其处理JavaScript和TypeScript文件,而无需使用其他加载器。请记住,与相反ts-loader,底层@babel/plugin-transform-typescript插件不执行任何类型检查。

3、Source Maps

想要了解 source map 的更多信息,请查看 开发 指南。

想要启用 source map,我们必须配置 TypeScript,以将内联的 source map 输出到编译后的 JavaScript 文件中。必须在 TypeScript 配置中添加下面这行:

tsconfig.json

  • {
  • "compilerOptions": {
  • "outDir": "./dist/",
  • + "sourceMap": true,
  • "noImplicitAny": true,
  • "module": "commonjs",
  • "target": "es5",
  • "jsx": "react",
  • "allowJs": true
  • }
  • }

现在,我们需要告诉 webpack 提取这些 source map,并内联到最终的 bundle 中。

webpack.config.js

  • const path = require('path');
  • module.exports = {
  • entry: './src/index.ts',
  • + devtool: 'inline-source-map',
  • module: {
  • rules: [
  • {
  • test: /\.tsx?$/,
  • use: 'ts-loader',
  • exclude: /node_modules/,
  • },
  • ],
  • },
  • resolve: {
  • extensions: [ '.tsx', '.ts', '.js' ],
  • },
  • output: {
  • filename: 'bundle.js',
  • path: path.resolve(__dirname, 'dist'),
  • },
  • };

查看 devtool 文档以了解更多信息。

4、使用第三方类库

在从 npm 安装 third party library(第三方库) 时,一定要记得同时安装此 library 的类型声明文件(typing definition)。你可以从 TypeSearch 中找到并安装这些第三方库的类型声明文件。

举个例子,如果想安装 lodash 类型声明文件,我们可以运行下面的命令:

  • npm install --save-dev @types/lodash

想了解更多,可以查看 这篇文章

5、导入其他资源

想要在 TypeScript 中使用非代码资源(non-code asset),我们需要告诉 TypeScript 推断导入资源的类型。在项目里创建一个 custom.d.ts 文件,这个文件用来表示项目中 TypeScript 的自定义类型声明。我们为 .svg 文件设置一个声明:

custom.d.ts

  • declare module '*.svg' {
  • const content: any;
  • export default content;
  • }

H这里,我们通过指定任何以 .svg 结尾的导入(import),将 SVG 声明(declare) 为一个新的模块(module),并将模块的 content 定义为 any。我们可以通过将类型定义为字符串,来更加显式地将它声明为一个 url。同样的概念适用于其他资源,包括 CSS, SCSS, JSON 等。

6、构建性能

Warning

这可能会降低构建性能。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐