webpack

 timeline
 title History of webpack
 2016 : v1.14.0
 2024 : v5.90.3

webpack 建议本地安装,可以在引入重大更新(breaking change)版本时,更容易分别升级项目。通常会通过运行一个或多个 npm scripts 以在本地 node_modules 目录中查找安装的 webpack。

npm v5.2.0 或更高版本,要运行 npx webapck 执行

安装 wepack

1
npm i webpack webpack-cli webpack-dev-server --save-dev
  • webpack webpack 的核心文件
  • webpack-cli webpack-cli webpack 脚本架,命令行工具,用于通过命令行使用 webpack 进行构建和打包前端项目
  • webpack-dev-server dev 开发环境进行项目实时预览的服务器

创建测试文件

1
2
3
4
├── src
|——├── main.js
|—— index.html
└── package.json // 包名:不能用 `webapck`

打包测试

Node 8.2/npm 5.2.0 及以上版本提供的 npx 命令,可以运行在最初安装的 webpack 包中的 webpack 二进制文件(即 ./node_modules/.bin/webpack)

1
$ npx webapck main.js --mode=development

生产环境,打包且压缩

1
$ npx webapck main.js --mode=production

配置 webpack

mode(模式)

1
2
3
module.exports = {
mode: 'development' // 'production' 开发模式/生产模式
}

entry(入口)

webpack 中有多种方式定义 entry 方式

1
2
3
4
// node 环境运行,module.exports 属于 commonjs 规范
module.exports = {
entry: './src/index.js', // 打包入口文件
}

output(输出)

告知 webpack 向硬盘写入编译文件的具体信息,只能有一个 output 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module.exports = {
...,
output: { // 打包输出配置
path: path.resolve(__dirname, 'dist'), // 打包输出路径
filename: '/assets/js/bundle.js', // 指定打包的 js 文件输出的路径及文件名
environment: { // arrowFunction?, asyncFunction?, bigIntLiteral?, const?, destructuring?, dynamicImport?, dynamicImportInWorker?, forOf?, globalThis?, module?, optionalChaining?, templateLiteral?
arrowFunction: false // 打包不允许使用箭头函数,兼容低版本浏览器
},
/**
* 删除 dist 目录重新打包,
* webpack 5.20.0+ 以上版本新增
* 5.20.0 以下版本,可采用 CleanWebpackPlugin 插件实现
*/
clean: true,
/**
* [name] 设置后为此 chunk 的名称,否则使用 chunk 的 ID
* ...(https://www.webpackjs.com/configuration/output/#template-strings)
*/
chunkFilename: 'static/js/[name].chunk.js',
/**
* [hash] 模块的 hash 值,hash 值长度 8 位
* [query] 带前缀 ? 的 query
* [ext] 带前缀 . 的扩展名
* ...(https://www.webpackjs.com/configuration/output/#template-strings)
*/
assetModuleFilename: '/static/media/[hash:8][ext][query]'
},
...
}

devServer

配置本地运行环境

1
2
3
4
5
// 开发模式下,提供虚拟服务器,用于项目开发和测试
devServer: {
port: 3000,
contentBase: "./dist" // boolean/string/array 服务器资源根目录
}

HRM —— webpack 设置热更新

模块热替换(HRM - hot module replacement),在程序运行过程中,替换、添加或删除模块,无需加载整个页面。
启动模块热替换功能,在构建失败时不刷新页面作为回退。

热更新 HRM 配置方法一

  1. package.json 配置
    1
    "dev": "webpack-dev-server --mode development --hot"
  2. 入口文件增加
    1
    2
    3
    if (module.hot) {
    module.hot.accept(file)
    }

热更新 HRM —— 第二种方式

1
2
3
4
5
6
7
8
9
10
var webpack = require('webpack')
module.exports = {
devServer: {
contentBase: './dist',
hot: true // 启动热更新
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}

热更新——第三种方式
Cannot find module 'webpack-cli/bin/config-yargs'
降低 webpack-cli@3 使用 webpack-dev-server

以上三种方式都没有办法解决,多次修改,浏览器自动刷新修改。都是需要重新启动 webpack

TS2339: Property ‘hot’ does not exist on type ‘NodeModule’. eslint 语法检测问题导致

1
2
3
4
5
6
7
8
  if ((module as any).hot) {
(module as any).hot.accept('./es6', () => {
sum(1, 2)
});
(module as any).hot.accept('./hrm', () => {
hot()
})
}

ERROR in [eslint] 13:13 error Parsing error: Unexpected token as. eslint 语法检测问题导致

安装依赖包

1
$ npm i @typescript-eslint/parser @typescript-eslint/eslint-plugin -D

1
2
3
4
5
6
7
  // .eslintrc.js
// 使用 typesciprt 解析器
module.exports = {
parser: '@typescript-eslint/parser',
...
}

module

webpack 需要处理的模块间的依赖关系包括:

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD define 和 require 语句
  • css/sass/less 文件中的 @import 语句。
  • stylesheet url(…) 或者 HTML <img src=…> 文件中的图片链接。

module.rules

创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。这些规则能够对模块应用 loader
注意: rule.test 值是正则,不是字符串正则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module.exports = {
...,
module: {
rules: [
{ // 处理 js
"test": /\.ts$/,
"use": [...] // 使用哪些 loader 进行编译
},
{ // 处理 css
"test": /\.css$/,
"use":[...]
},
{ // 处理图片
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
type: 'asset', // 用于匹配模块
parser: {
dataUrlCondition: { // 当加哉的图片资源大于最大限制数时,图片转换为 base64
maxSize: imageInlineSizeLimit,
},
},
generator: {
filename: 'assets/images/[hash:10][ext][query]' // 图片资源输出目录
}
},
]
}
}

loader(转换器)

通过 loader 可以使 webpack 支持多种语言和预处理器语法编写的模块
loader 向 webapck 描述了如何处理非原生模块,并将相关依赖引入到 bundles 文件中。

样式转换器

  • tyle-loader 将 css 通过创建 style 标签插入 DOM
  • css-loader 对 @import 和 url() 进行处理,将 css 资源编译成 commonjs 的模块到 js 中
  • stylus-loader / less-loader / sass-loader 都是 css 预处理器,任选其一,scss 稍有不同,以 sass 为例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ...
    module: {
    rules: [
    {
    test: /\.css$/,
    /**
    * style-loader 将 css 插入 DOM
    * css-loader 对 @import 和 url() 进行处理
    */
    use: ['style-loader', 'css-loader']
    },
    {
    test: /\.s[ac]ss$/,
    use: ['style-loader', 'css-loader', 'sass-loader']
    }
    ]
    }
    ...
    Module not found: Error: Can’t resolve ‘style-loader!css-loader’
    加载器解析顺序从右到左,从下到上

关于 —— bable 通过 loader 实现 import 导入任何类型模块,如 .jsx/.css/.less 等

npm i @babel/core @babel/preset-env @babel/preset-react babel-loader -D
* @babel/core 是调用 babel 的 API 进行转码的包
* babel-loader 执行转义的核心包
* babel-preset-env 一个新的 preset,根据配置目标运行环境启用需要的 babel 插件
* babel-preset-react 用于转义 react 的 JSX 语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// webpack.config.js
module: {
// 配置模块规则 —— 配置 loader
rules: [
{
test: /\.css$/i,
use: [
'style-loader', //把 css 插入 Dom
]
},
// webpack 打包时识别后缀为 .js|.jsx 的文件,并用 babel-loader 去转换
{
test: /\.(js|jsx)$/, //告诉 webpack 需要处理的对象
exclude: /node_modules/,
use: [ // 告诉 webpack 用什么来处理,loader 解析方式是从右到左(从后到前)应用
{ // babel 配置
loader: "babel-loader", // 加截器——ES2015+ 代码并将其转换为 ES5
options: {
// 设定预置环境
presets: [
// 编译环境插件
["@babel/preset-env",
// 配置
{
// 指定浏览器运行版本
"targets": {
"chrome": '86',
"ie": "11"
},
"corejs": "3", // useBuiltIns 为 false 时,corejs 不生效
// corejs 使用 “usage" 按需加载
"useBuiltIns": "usage"
}
]
]
},
},
]
}
]
}

配置相应内容,告诉 babel-loader 使用 ES6 和 JSX 插件

1
2
3
4
// .babelrc
{
"presets": ['env', 'react']
}

plugins(插件)

clean-webpack-plugin

webapck4 clean-webpack-plugin 删除 build 或 dist 下的文件,生新的文件
使用方式不同版本各有不同,建议参考文档

1
2
3
4
5
6
7
// webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin();
]
}

注意:webpack5 不需要使用 clean-wepack-plugin 插件,只需要做如下配置即可

1
2
3
4
5
6
7
...
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'asset/js/bundle.js', // 生成的 js 文件的存储位置
clean: true // 删除 dist 重新打包
},
...

fork-ts-checker-webpack-plugin

Webapck plugin that runs TypeScript type checker on a separate process.
独立进程上运行 TypeScript type checker 的 webpack 插件。

Optimization 优化

根据选择的 mode 执行不同的优化,所有优化必须手动配置和重写。

optimization.minimizer

通过提供一个或多个定制过的 TerserPlugin,覆盖默认压缩工具(minimizer)

Error

Cannot find module ‘webpack-cli/bin/config-yargs’

code: ‘MODULE_NOT_FOUND’

webpack-dev-server –open –mode development
“webpack-cli”: “^4.6.0”

降版本,将 webpack-cli 版本降至 3

1
$ npm i webpack-cli@3 -D --force

解析-resolve

配置模块如何解析

1
2
3
4
5
6
7
8
// webpack.dev.config.js
module.exports = {
...
resolve: {
extensions: ['.ts', '.js'] // 尝试按顺序解析这些后缀名。如果有多个文件有相同的名字,但后缀名不同,webpack 会解析列在数组首位的后缀的文件 并跳过其余的后缀。
}
...
}

Module not found: Error: Can’t resolve ‘os’ in ‘/Users/zhangliping/Documents/Project/webpack/src’ ts 文件中获取 os 模块失败

1
2
3
4
5
6
7
8
9
10
11
// webpack.dev.config.js
module.exports = {
...
resolve: {
fallback: { // 当正常解析失败时,重定向模块请求。
os: require.resolve("os-browserify/browser"),
},
...
}
...
}