webpack是一个现代JavaScript应用程序的静态(static)模块(module)打包器(bundler)。

  • “静态(static)”: 即代码被打包成静态资源,可部署到静态Web服务器
  • “模块(module)”: webpack默认支持各种模块化开发,ES Module、CommonJS、AMD、CMD等
  • “打包器(bundler)”: webpack被用于现代基于JavaScript的前端应用的构建打包

1.初识webpack

1.1 为什么需要webpack

Webpack为现代前端开发提供了强大的构建工具:

  1. 模块化开发: 支持代码模块化,提高可维护性和复用性。
  2. 使用高级特性: 允许使用ES6+、TypeScript等高级语法,通过转译确保浏览器兼容性。
  3. 样式预处理器: 集成sass、less等样式预处理器,提升样式代码可维护性。
  4. 实时刷新: 提供开发服务器(devserver)和热模块替换(HMR),实现实时刷新和模块替换,提高开发效率。
  5. 代码优化: 提供压缩、合并等优化插件,用于减小生产环境代码体积。
  6. 资源管理: 将各种资源视为模块(module),通过加载器(loader)进行处理和管理。

Webpack通过这些功能和插件,使前端开发更高效、更灵活,适应了现代前端项目的复杂性。

1.2 使用nvm安装NodeJS

首先安装nvm,nvm是一个NodeJS多版本管理工具。nvm管理的多个node版本位于~/.nvm路径下。

nvm支持多种安装方式,具体可参考Installing and Updating。 这里使用Git安装nvm。

 1cd ~
 2
 3git clone https://github.com/nvm-sh/nvm.git .nvm
 4
 5cd ~/.nvm
 6
 7# checkout最新版本
 8git checkout v0.39.7
 9
10# 将以下环境变量配置 ~/.bashrc、~/.profile 或 ~/.zshrc 文件中,以便在登录时自动加载它(你可能需要在上述文件中添加一个或多个)
11vi ~/bash_profile
12export NVM_DIR="$HOME/.nvm"
13  [ -s "/usr/local/opt/nvm/nvm.sh" ] && . "/usr/local/opt/nvm/nvm.sh"  # This loads nvm
14  [ -s "/usr/local/opt/nvm/etc/bash_completion" ] && . "/usr/local/opt/nvm/etc/bash_completion"  # This loads nvm bash_completion

使用nvm安装LTS版本NodeJS,并切换使用LTS版本NodeJS:

1nvm install --lts
2
3nvm use --lts
4
5node -v
6v20.10.0

1.3 webpack初体验

使用npm init初始一个项目。

1mkdir my-project
2cd my-project
3npm init -y

安装webpack和webpack-cli:

1npm install webpack webpack-cli --save-dev

在my-project目录中创建webpack的配置文件webpack.config.js

 1const path = require('path')
 2
 3module.exports = {
 4    mode: 'production',
 5    entry: './src/index.js',
 6    output: {
 7        path: path.resolve(__dirname, 'dist'),
 8        filename: 'bundle.js'
 9    }
10}

webpack.config.jsentry用来指定webpack的打包入口,webpack根据入口文件生成打包时的依赖图

output用于指定打包后的输出文件的相关配置。它定义了打包生成的文件的存放位置、文件名等信息。

注意在webpack.config.js中使用了Node内置的path模块,用于处理文件路径和目录路径,以确保它们在不同操作系统下都能正确工作。

在my-project目录中创建源码目录src,在src下创建源码文件index.js和foo.js。

foo.js内容如下:

1export function foo() {
2    return 'foo'
3}

index.js内容如下:

1import { foo } from './foo'
2
3console.log(foo())

编辑package.json,在npm的scripts中加入build脚本:

 1{
 2  "name": "my-project",
 3  "version": "1.0.0",
 4  "description": "",
 5  "main": "index.js",
 6  "scripts": {
 7    "build": "webpack"
 8  },
 9  "keywords": [],
10  "author": "",
11  "license": "ISC",
12  "devDependencies": {
13    "webpack": "^5.89.0",
14    "webpack-cli": "^5.1.4"
15  }
16}

my-project目录中运行npm run build可进行构建打包。会在dist目录中生成bundle.js

1.4 webpack的依赖图

Webpack在处理应用程序时,首先根据命令或配置文件找到项目的入口文件。从入口文件开始,Webpack构建一个依赖关系图,该图包含了应用程序中所有需要的模块,例如.js文件、css文件、图片、字体等。

webpack.png

这个依赖关系图是一个表示模块之间关系的结构,Webpack通过分析模块之间的依赖关系,了解它们之间的引用和依赖。这使得Webpack能够正确地了解整个应用程序的结构,并知道如何逐一打包这些模块。

依赖图的遍历过程是Webpack分析和处理模块的核心。Webpack会递归地遍历依赖关系图,根据文件类型使用相应的loader来解析每个模块。loader负责将不同类型的文件转换为Webpack可理解的模块,例如将ES6+代码转换为ES5,或将CSS转换为JavaScript对象。

通过这种方式,Webpack逐个处理每个模块,确保它们正确地被转换和打包。最终,Webpack生成最终的静态资源,可供部署到服务器上,实现整个项目的打包和构建。

2.webpack的基本概念

webpack本质上是现代JavaScript应用程序的静态模块打包工具。当webpack处理应用程序时,它会从一个或多个入口点内部构建一个依赖图,然后将项目所需的每个模块组合成一个或多个包(bundle),这些包是用于提供的内容的静态资源。

2.1 入口(entry)

在webpack.config.js中entry用来指定webpack的打包入口。

入口点表示webpack应该使用哪个模块来开始构建其内部依赖关系图。Webpack将确定入口点依赖的其他模块和库(直接和间接)。

默认情况下,它的值是./src/index.js,但你可以通过在webpack.config.js中设置entry属性来指定不同的(或多个)入口点。例如:

1module.exports = {
2  entry: './path/to/my/entry/file.js',
3};

2.2 输出(output)

output用来指定webpack打包的输出,即在哪里保存打包的输出以及如何命名这些文件。对于主输出文件,默认为./dist/main.js,对于任何其他生成的文件,默认为./dist文件夹。

1const path = require('path');
2
3module.exports = {
4  entry: './path/to/my/entry/file.js',
5  output: {
6    path: path.resolve(__dirname, 'dist'),
7    filename: 'my-first-webpack.bundle.js',
8  },
9};

2.3 loaders

webpack默认只支持js和json两种文件类型,可以通过loader实现对其他类型的文件的支持并将它们转换成有效的模块添加到依赖图中。loader本身是一个处理函数,将源文件作为参数并返回转换后的结果。

webpack 配置中的 loader 具有两个属性:

  • test属性标识应该被转换的文件或文件集。
  • use属性指示应该使用哪个loader进行转换。
 1const path = require('path');
 2
 3module.exports = {
 4  output: {
 5    filename: 'my-first-webpack.bundle.js',
 6  },
 7  module: {
 8    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
 9  },
10};

上面的配置定义了一个包含两个必需属性(test 和 use)的单一模块的规则(rules)属性。这告诉 webpack 编译器以下内容:

“嘿,webpack 编译器,当你遇到一个由 require()/import 语句解析为 ‘.txt’ 文件的路径时,在将其添加到 bundle 之前,请使用 raw-loader 进行转换。”

常用的loader:

  • babel-loader - 转换ES6+
  • css-loader - css文件的加载和解析
  • less-loader - 将less转换成css
  • file-loader - 进行图片、字体的打包
  • raw-loader - 将文件以字符串的形式导入
  • thread-loader - 多进程打包JS和CSS

更多内容可查看这里

2.4 plugins

虽然loader用于转换特定类型的模块,但plugin可以用于执行更广泛的任务,例如bundle优化、asset管理和注入环境变量。

使用插件,需要使用require()引入它并将其添加到plugins数组中。大多数插件可以通过选项进行自定义。由于可以在配置中多次使用插件以实现不同的目的,因此需要通过使用new运算符调用插件来创建其实例。

1const HtmlWebpackPlugin = require('html-webpack-plugin');
2const webpack = require('webpack'); //to access built-in plugins
3
4module.exports = {
5  module: {
6    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
7  },
8  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
9};

在上面的示例中,html-webpack-plugin为应用程序生成一个HTML文件,并自动将所有生成的bundle注入到该文件中。

webpack提供了许多开箱即用的插件,可用的插件列表见这里

2.5 mode

通过将mode参数设置为developmentproductionnone中的一个,可以启用与每个环境对应的webpack内置优化。默认值是production(什么都不设置的情况下)。

1module.exports = {
2  mode: 'production',
3};
  • development 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development. 为模块和 chunk 启用有效的名。
  • production 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。为模块和 chunk 启用确定性的混淆名称,FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin 和 TerserPlugin 。
  • none 不使用任何默认优化选项

更多有关mode配置的信息,详见这里

2.6 浏览器兼容性

Webpack支持所有符合ES5标准的浏览器(不支持IE8及以下)。Webpack需要Promise来处理import()require.ensure()。如果要支持较旧的浏览器,需要在使用这些表达式之前加载一个polyfill

Webpack需要Promise 来处理动态导入(import())和代码拆分(require.ensure())等异步加载的语法。由于一些较老的浏览器不原生支持 Promise,因此需要使用一个 Polyfill 来提供对 Promise 的支持,以确保应用在这些浏览器中正常运行。Polyfill 通常是通过加载额外的 JavaScript 代码来实现的。

2.7 Modules

在模块化编程中,开发人员将程序拆分为称为模块的离散功能块。

每个模块的表面积较小,比完整程序小,使得验证、调试和测试变得简单。编写良好的模块提供了坚实的抽象和封装边界,使得每个模块在整个应用程序中都具有一致的设计和明确的目的。

Node.js几乎从一开始就支持模块化编程。然而,在Web上,对模块的支持进展缓慢。存在多个工具支持Web上的模块化JavaScript,具有各种优点和局限性。Webpack借鉴了从这些系统中学到的经验教训,并将模块的概念应用于项目中的任何文件。

webpack的模块与Node.js模块相比,可以以多种方式表达其依赖关系。一些例子包括:

  • ES2015的import语句
  • CommonJS的require()语句
  • AMD的definerequire语句
  • 在css/sass/less文件中的@import语句
  • 样式中的图像URL,如url(...),或者HTML文件中的<img src=...>

Webpack原生支持以下模块类型:

  • ECMAScript模块
  • CommonJS模块
  • AMD模块
  • Assets资源
  • WebAssembly模块

除此之外,webpack通过加载器(loader)还支持用多种语言和预处理器编写的模块。加载器告诉webpack如何处理非原生模块,并将这些依赖关系包含到您的捆绑文件中。webpack社区已经为许多流行的语言和语言处理器构建了加载器,包括:

  • CoffeeScript
  • TypeScript
  • ESNext(Babel)
  • Sass
  • Less
  • Stylus
  • Elm
  • 还有许多其他语言!总体而言,webpack提供了丰富而强大的自定义API,使您可以在任何堆栈中使用webpack,同时对您的开发、测试和生产工作流程保持无偏见。

要获取完整列表,请参阅加载器列表或自行编写加载器。

3. html-webpack-plugin

HtmlWebpackPlugin简化了为webpack bundle文件创建HTML文件的过程。这对于webpack bundle文件名中包含会在每次编译时改变的hash值的情况特别有用。 可以让该插件生成一个HTML文件,也可以使用lodash模板提供自己的模板,或者使用自己的loader。

my-project项目上安装html-webpack-plugin:

1npm install html-webpack-plugin -D

webpack.config.js中配置插件:

 1const path = require('path')
 2
 3const HtmlWebpackPlugin = require('html-webpack-plugin');
 4
 5module.exports = {
 6    mode: 'production',
 7    entry: './src/index.js',
 8    output: {
 9        path: path.resolve(__dirname, 'dist'),
10        filename: 'bundle.js',
11        clean: true
12    },
13    plugins: [new HtmlWebpackPlugin({
14        title: "my-project"
15    })],
16}

执行npm run build后,dist目录中会生成一个名称为index.html的HTML文件,其中包含所有webpack bundle的内容,并使用script标签将它们嵌入到了文件中。

dist/index.html:

 1<!DOCTYPE html>
 2<html>
 3<head>
 4  <meta charset="utf-8">
 5  <title>my-project</title>
 6  <meta name="viewport" content="width=device-width, initial-scale=1">
 7  <script defer="defer" src="bundle.js"></script>
 8</head>
 9<body>
10</body>
11</html>

上面的html是自动生成的,HtmlWebpackPlugin的配置还支持使用定制的html模板文件,更多内容查看这里

4.webpack-dev-server

webpack提供了本地开发服务器的功能。webpack-dev-server可以用于快速开发应用程序。

my-project项目上安装:

1npm install webpack-dev-server -D

webpack.config.js中配置devServer:

 1const path = require('path')
 2
 3const HtmlWebpackPlugin = require('html-webpack-plugin');
 4
 5module.exports = {
 6    mode: 'production',
 7    entry: './src/index.js',
 8    devServer: {
 9        hot: true,
10        host: "0.0.0.0",
11        port: 9090,
12    },
13    output: {
14        path: path.resolve(__dirname, 'dist'),
15        filename: 'bundle.js',
16        clean: true
17    },
18    plugins: [new HtmlWebpackPlugin({
19        title: "my-project"
20    })],
21}

在package.json中配置dev脚本:

 1{
 2  "name": "my-project",
 3  "version": "1.0.0",
 4  "description": "",
 5  "main": "index.js",
 6  "scripts": {
 7    "build": "webpack",
 8    "dev": "webpack serve"
 9  },
10  "keywords": [],
11  "author": "",
12  "license": "ISC",
13  "devDependencies": {
14    "html-webpack-plugin": "^5.6.0",
15    "webpack": "^5.89.0",
16    "webpack-cli": "^5.1.4",
17    "webpack-dev-server": "^4.15.1"
18  }
19}

运行npm run dev启动devserver:

1npm run dev

浏览器打开http://127.0.0.1:9090访问my-project应用程序。

5.常用loader

5.1 css-loader

创建src/css/style.css文件:

1.content {
2    color: blue;
3}

修改src/foo.js文件:

1import "./css/style.css"
2
3export function foo() {
4    const divEl = document.createElement('div')
5    divEl.innerText = "Hello World"
6    divEl.className = "content"
7    document.body.appendChild(divEl)
8}

修改src/index.js

1import { foo } from "./foo"
2
3foo()

此时执行npm run build会报下面的错误,提示缺少loader处理css文件:

1ERROR in ./src/css/style.css 1:0
2Module parse failed: Unexpected token (1:0)
3You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
4> .content {
5|     color: blue;
6| }
7 @ ./src/foo.js 1:0-24
8 @ ./src/index.js 1:0-27 3:0-3

安装css-loader:

1npm install css-loader -D

webpack.config.js中配置使用css-loader:

 1const path = require('path')
 2
 3const HtmlWebpackPlugin = require('html-webpack-plugin');
 4
 5module.exports = {
 6    mode: 'production',
 7    entry: './src/index.js',
 8    devServer: {
 9        hot: true,
10        host: "0.0.0.0",
11        port: 9090,
12    },
13    output: {
14        path: path.resolve(__dirname, 'dist'),
15        filename: 'bundle.js',
16        clean: true
17    },
18    module: {
19        rules: [
20            {
21                test: /\.css$/i,
22                use: ["css-loader"],
23            },
24        ],
25    },
26    plugins: [new HtmlWebpackPlugin({
27        title: "my-project"
28    })],
29}

此时运行npm run build不再报错。npm run dev启动devserver后,访问应用,页面上的Hello World文字并没有应用上蓝色的样式。

这是因为css-loader只是负责将.css文件进行解析为webpack module,并不会将解析之后的css插入到页面中。

如果希望再完成插入style的操作,那么还需要另外一个style-loader;

5.2 style-loader

安装style-loader:

1npm install style-loader -D

webpack.config.js中配置使用css-loader:

 1    // ...
 2    module: {
 3        rules: [
 4            {
 5                test: /\.css$/i,
 6                use: ["style-loader", "css-loader"],
 7            },
 8        ],
 9    },
10    // ...

注意因为loader的执行顺序是从右向左,所以需要将style-loader写到css-loader的前面。

npm run dev启动devserver后,访问应用,页面上的Hello World文字应用上了蓝色的样式。

注意style-loader的默认配置是将样式以<style>标签的形式添加到页面中的。这是因为style-loaderinjectType配置默认值为styleTag。另外还支持其他形式,例如linkTag的形式将css抽取到单独的css文件中。

5.3 less-loader, sass-loader, stylus-loader

在开发中,可能会使用less、sass、stylus的预处理器来编写css样式,有助于提高CSS代码的可维护性、可读性和可重用性,使得开发和维护CSS更加高效。

为了使项目支持这些预处理器,需要将基于这些预处理器编写的css转换成普通的css,对应webpack中的less-loader, sass-loader, stylus-loader。

以less为例:

1npm install less-loader -D

webpack.config.js:

 1module.exports = {
 2  module: {
 3    rules: [
 4      {
 5        test: /\.less$/i,
 6        use: [
 7          // compiles Less to CSS
 8          "style-loader",
 9          "css-loader",
10          "less-loader",
11        ],
12      },
13    ],
14  },
15};

5.4 postcss-loader

PostCSS 是一个使用JavaScript插件转换样式的工具,主要用于对CSS进行处理和转换。它采用插件架构,允许使用各种插件来执行不同的样式转换操作。这使得开发者可以选择性地使用和组合插件,以满足项目的需求。

PostCSS主要特性包括:

  1. 插件架构: PostCSS 的核心是插件系统,每个插件执行一个特定的任务。插件可以用于处理、转换、优化或添加样式规则,使得样式处理过程更加灵活。

  2. 支持现代 CSS 特性: PostCSS 支持最新的CSS规范,包括 CSS Variables、Custom Properties、Custom Selectors等。这使得开发者可以使用最新的CSS功能而无需担心浏览器兼容性。

  3. Autoprefixer: PostCSS生态系统中著名的插件之一,它可以自动为CSS添加浏览器前缀,提高样式在不同浏览器中的一致性。

  4. 代码压缩: 使用PostCSS插件,可以对 CSS 进行代码压缩,减小文件大小,提高页面加载性能。

  5. 构建过程集成: PostCSS通常被集成到构建过程中,例如通过构建工具(Webpack、Rollup)或任务运行器(Gulp、Grunt)。这使得样式的处理和转换能够自动化,并与其他构建步骤协同工作。

PostCSS 提供了一个灵活、可定制的方式来处理和转换CSS,使得开发者能够适应不断发展的前端技术和标准。

Webpack对PostCSS的支持,对应的是postcss-loader。

使用PostCSS时,不得不提到它的一个叫postcss-preset-env的插件。postcss-preset-env 是一个PostCSS插件,它提供了一个预设环境,让你可以使用未来 CSS 特性,而无需等待浏览器支持。这样,你可以更早地体验和使用最新的CSS语法和功能。 通过 postcss-preset-env,你可以在项目中配置所需的浏览器兼容性,以确保生成的CSS在目标浏览器上具有正确的前缀。它可以帮助我们将一些现代的CSS特性,转成大多数浏览器认识的CSS,并且会根据目标浏览器或者运行时环境添加所需的polyfill, 也包括会自动帮助我们添加autoprefixer(所以相当于已经内置了autoprefixer)。

安装:

1npm install postcss-loader -D
2npm install postcss-preset-env -D

webpack.config.js:

 1const path = require('path')
 2
 3const HtmlWebpackPlugin = require('html-webpack-plugin');
 4
 5module.exports = {
 6    mode: 'production',
 7    entry: './src/index.js',
 8    devServer: {
 9        hot: true,
10        host: "0.0.0.0",
11        port: 9090,
12    },
13    output: {
14        path: path.resolve(__dirname, 'dist'),
15        filename: 'bundle.js',
16        clean: true
17    },
18    module: {
19        rules: [
20            {
21                test: /\.css$/i,
22                use: ["style-loader", "css-loader", "postcss-loader"],
23            },
24        ],
25    },
26    plugins: [new HtmlWebpackPlugin({
27        title: "my-project"
28    })],
29}

postcss.config.js中配置所需的postcss插件,这里使用预设插件postcss-preset-env:

 1module.exports = {
 2    plugins: [
 3        [
 4            "postcss-preset-env",
 5            {
 6                // Options
 7            },
 8        ],
 9    ],
10}

为了测试postcss-preset-env的效果,可以使用一些未来的CSS特性并查看生成的兼容性样式。

修改src/css/style.css如下:

1.content {
2    color: blue;
3
4    &:hover {
5        color: red;
6    }
7
8}

在这个示例中,我们使用了CSS Nesting(嵌套)。通过使用postcss-preset-env,它将会被处理为兼容性的CSS样式,确保浏览器正确地解释这些样式。

运行npm run dev,浏览器打开http://127.0.0.1:9090访问my-project应用程序。使用开发者工具查看.content样式,会发现其被postcss转换成了:

1.content {
2    color: blue;
3
4}
5
6.content:hover {
7        color: red;
8}

5.5 babel-loader

Babel是一个用于JavaScript代码转换的工具。它属于JavaScript编译器家族的一部分,用于将 ECMAScript 2015+ 版本的代码(通常称为ES6+)转换为向后兼容的 JavaScript 版本,以便在不同环境中运行。这使得开发者能够在使用最新ECMAScript特性的同时,仍然确保他们的代码在更旧的JavaScript环境中正常运行。

Babel 不仅限于将新的语法转换为旧的语法,还支持插件系统,允许开发者添加自定义的转换规则。这使得 Babel 成为一个灵活且可扩展的工具,能够适应不同项目和需求。

常见用例包括将最新的JavaScript 特性(如箭头函数、模板字符串、解构赋值等)转换为向后兼容的版本,以及处理不同浏览器或Node.js版本之间的兼容性问题。

Babel通过插件的形式支持各种各样的转换,但是如果项目需求的转换过多,一个个设置是比较麻烦的,我们可以使用预设(preset)。更多有关Babel预设的内容可查看Babel官方文档。Babel官方提供了以下预设:

  • @babel/preset-env 用于编译ES2015+语法
  • @babel/preset-typescript 用于TypeScript
  • @babel/preset-react 用于React
  • @babel/preset-flow 用于Flow

Babel在webpack中对应babel-loader,使用下面的命令在my-project项目中安装:

1npm install babel-loader @babel/core @babel/preset-env -D

webpack.config.js:

 1const path = require('path')
 2
 3const HtmlWebpackPlugin = require('html-webpack-plugin');
 4
 5module.exports = {
 6    mode: 'production',
 7    entry: './src/index.js',
 8    devServer: {
 9        hot: true,
10        host: "0.0.0.0",
11        port: 9090,
12    },
13    output: {
14        path: path.resolve(__dirname, 'dist'),
15        filename: 'bundle.js',
16        clean: true
17    },
18    module: {
19        rules: [
20            {
21                test: /\.css$/i,
22                use: ["style-loader", "css-loader", "postcss-loader"],
23            },
24            {
25                test: /\.js$/i,
26                exclude: /node_modules/,
27                use: ["babel-loader"],
28            }
29        ],
30    },
31    plugins: [new HtmlWebpackPlugin({
32        title: "my-project"
33    })],
34}

babel.config.js:

1module.exports = {
2    presets: [
3        ['@babel/preset-env', {
4            "targets": "> 0.25%, not dead"
5        }]
6    ]
7}

这个Babel的配置使用了@babel/preset-env预设,通过targets选项指定了编译的目标浏览器,其中设定了对 Chrome 58 及以上版本和 Internet Explorer 10 及以上版本的兼容性。useBuildIns: "usage"的设置表示按需引入必要的 polyfill,以满足目标浏览器对语言特性的需求。

6.asset module

Webpack内置支持很多module,Assets module就是其中之一。

Asset Modules允许您使用资源文件(字体、图标等),而无需配置额外的loader。

在webpack 5之前,通常使用以下方式:

  • 使用raw-loader将文件导入为字符串
  • 使用url-loader将文件内联到bundle中作为数据URI
  • 使用file-loader将文件输出到输出目录

Asset Modules类型通过添加4种新的模块类型取代了所有这些loader:

  • asset/resource 输出一个单独的文件并导出URL,以前可以通过使用file-loader实现。
  • asset/inline 导出资源的数据URI,以前可以通过使用url-loader实现。
  • asset/source 导出资源的源代码,以前可以通过使用raw-loader实现。
  • asset 自动选择是输出数据URI还是输出一个单独的文件,以前可以通过使用url-loader并设置资产大小限制来实现。

webpack.config.js:

 1    module: {
 2        rules: [
 3            // ...
 4            {
 5                test: /\.(png|jpe?g|svg|gif)$/,
 6                type: "asset",
 7                parser: {
 8                    dataUrlCondition: {
 9                        maxSize: 4 * 1024 // 4kb
10                    }
11                },
12                generator: {
13                    filename: "img/[name]_[hash:8][ext]"
14                }
15            }
16            // ...
17        ]
18    }

这里配置对/\.(png|jpe?g|svg|gif)$/的图片资源类型使用了类型为asset的module,generator指定了输出的文件名:

  • [ext]: 处理文件的扩展名
  • [name]:处理文件的名称
  • [hash]:文件的内容,使用MD4的散列函数处理,生成的一个128位的hash值(32个十六进制)

parser.dataUrlCondition.maxSize指定了转换的最大图片大小,开发中我们往往是小的图片需要转换,但是大的图片直接使用图片即可。

  • 因为小的图片转换base64之后可以和页面一起被请求,减少不必要的请求过程
  • 大的图片如果进行转换,反而会影响页面的请求速度

更多关于asset moudle的内容可以查看文档

7.模块解析

一个resolver是一个库,它通过绝对路径帮助定位一个模块。模块可以作为依赖关系从另一个模块中引用:

1import foo from 'path/to/module';
2// 或者
3require('path/to/module');

依赖模块可以来自应用程序代码或第三方库。resolver帮助webpack找到每个这样的require/import语句所需的模块代码,以便将其包含bundle的代码包中。webpack在打包模块时使用enhanced-resolve来解析文件路径。

webpack能解析三种文件路径:

  • 绝对路径: 由于我们已经有了文件的绝对路径,因此不需要进一步的解析。

    1import '/home/me/file';
    2
    3import 'C:\\Users\\me\\file';
    
  • 相对路径: 在这种情况下,import/require发生的源文件的目录被视为上下文目录。import/require中指定的相对路径与此上下文路径连接,以生成模块的绝对路径。

    1import '../src/file1';
    2import './file2';
    
  • 模块路径: 在resolve.modules中指定的所有目录中搜索模块(默认值是 ['node_modules'],所以默认会从node_modules中查找文件;)。以通过使用resolve.alias配置选项为其创建别名,以将原始模块路径替换为替代路径。

    1import 'module';
    2import 'module/lib/file';
    

根据上述规则解析路径后,解析器会检查路径指向文件还是目录。如果路径指向文件:

  • 如果路径具有文件扩展名,则直接打包文件。
  • 否则,将使用resolve.extensions选项解析文件扩展名,该选项告诉解析器哪些扩展名可接受进行解析,例如.js、.jsx

如果路径指向文件夹,则将执行以下步骤以找到具有正确扩展名的正确文件:

  • resolve.mainFiles的默认值是 ['index']
  • 再根据resolve.extensions来解析扩展名

根据构建目标,Webpack为这些选项提供了合理的默认值。

extensions和alias配置

  • extensions是解析到文件时自动添加扩展名, 默认值是['.wasm', '.mjs', '.js', '.json']
    • 如果代码中想要定制加载.vue, .jsx, .ts等文件时,乣配置上对应的扩展名;
  • 配置别名alias
    • 当项目的目录结构比较深时,可以给某些常见的路径起一个别名

具体例子如下:

 1module.exports = {
 2  // ...
 3  resolve: {
 4    extensions: ['.wasm', '.mjs', '.js', '.json', ".vue", ".jsx", ".ts", ".tsx"],
 5    alias: {
 6      "@": path.resolve(__dirname, "./src")
 7      pages: path.resolve(__dirname, "./src/pages")
 8    }
 9  }
10  // ...
11}

8.常用plugin

在Webpack中,插件(Plugins)和加载器(Loaders)是两个不同的概念:

  • Loader是用于特定的模块类型进行转换
  • Plugin可以用于执行更加广泛的任务,比如可以用于优化、压缩、处理资源、注入环境变量等各种构建过程中的任务。

8.1 HtmlWebpackPlugin

HtmlWebpackPlugin简化了为webpack bundle文件创建HTML文件的过程。前面已经介绍过了,这里略过。

8.2 DefinePlugin

DefinePlugin允许在编译时创建配置的全局常量,DefinePlugin是一个webpack内置的插件,不需要单独安装。

 1const { DefinePlugin } = require("webpack")
 2
 3module.exports = {
 4   plugins: [
 5      new webpack.DefinePlugin({
 6        PRODUCTION: JSON.stringify(true),
 7        VERSION: JSON.stringify('5fa3b9'),
 8        BROWSER_SUPPORTS_HTML5: true,
 9        TWO: '1+1',
10        'typeof window': JSON.stringify('object'),
11        'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
12      })
13   ]
14}

参考