Webpack-学习笔记

webpack 学习笔记

At its core, webpack is a static module bundler for modern JavaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every module your project needs and generates one or more bundles.
本质上,webpack 是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个依赖图 (dependency graph) ,此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。

项目搭建

webpack 是基于 nodejs 的,因此 webpack 项目也是一个 node 项目,直接使用 npm 来初始化项目;对于大多数项目,建议本地安装,便于后期分项目升级;

  1. 使用npm init命令初始化项目文件夹,创建package.json文件;
  2. 安装 webpack 和 webpack-cli 开发依赖;
1
2
> npm init -y
> npm install webpack webpack-cli -D

以该项目为例,目录结构如下:

1
2
3
4
5
6
7
8
9
10
.
├── .gitignore
├── README.md
├── LICENSE
├── package.json
├── package-lock.json
├── node_modules
├── src
| └── index.js
└── dist

简单使用

  1. 新建一个模块a.js,并在里面编写一个简单的打印函数;
1
2
3
4
5
6
7
8
// src/a.js
function print(params) {
console.log(params);
}

module.exports = {
print,
};
  1. 新建入口index.js,并引入模块a.js
1
2
3
4
// src/index.js
const moduleA = require("./a");

moduleA.print("Hello, world!");
  1. 运行 npx webpack 开始打包,结果将被输出到根目录下 dist 文件夹中,并得到以下输出:
1
2
3
4
5
6
7
8
9
10
asset main.js 224 bytes [compared for emit] [minimized] (name: main)
./src/index.js 83 bytes [built] [code generated]
./src/a.js 104 bytes [built] [code generated]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

webpack 5.37.1 compiled with 1 warning in 201 ms
  1. 得到打包好的输出文件dist/main.js

基本概念

  • 入口(entry):指示 webpack 应该使用哪个模块,来作为构建其内部依赖图(dependency graph)的开始,默认值是./src/index.js
  • 输出(output):告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在./dist文件夹中。
  • 加载器(loader):让 webpack 能够去处理 js 和 json 以外的其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
  • 插件(plugin):让 webpack 可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量等。
  • 模式(mdoe):通过选择developmentproductionnone之中的一个,来设置 mode 参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为production
  • 目标(target):告知 webpack 为部署目标指定一个环境。默认值为browserslist,如果没有找到browserslist的配置,则默认为web
  • 模块热替换(HMR - hot module replacement):是指在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。
  • 初始化块(initail chunk) 是入口起点的主块。此 chunk 包含为入口起点指定的所有模块及其依赖项。默认名为main.js
  • 非初始化块 (non-initial chunk)是可以延迟加载的块。可能会出现在使用 动态导入(dynamic imports) 或者 SplitChunksPlugin 时。默认情况下,这些非初始化块没有名称,因此会使用唯一 ID 来替代名称。

基本配置

webpack 可以无需使用任何配置文件。webpack 会假定项目的入口起点为src/index.js,然后会在dist/main.js输出结果,并且在生产环境开启压缩和优化。但通常我们还需要对 webpack 进行更精细化的配置,以充分发挥 webpack 的能力。

习惯性的将配置文件拆分为三个配置文件,并提取公共配置部分到webpack.common.js,然后分别将开发模式和生产模式的配置放在webpack.dev.jswebpack.prod.js中,并用webpack-merge合并公共部分的配置。

最后package.json中添加 webpack 的 script 启动命令,让 webpack 使用自定义的配置文件替代默认的webpack.config.js

package.json

1
2
3
4
"script" : {
"serve": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}

遵循不重复原则(Don’t repeat yourself - DRY),保留一个通用配置,将相同的公共配置放入webpack.common.js中:

webpack.common.js

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
42
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: {
index: './src/index.js',
print: './src/print.js',
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader',
}, ],
},
resolve: {
symlinks: false,
},
optimization: {
sideEffects: true,
usedExports: true,
splitChunks: {
chunks: 'async', // 对哪些块进行优化,all | async | initial,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};

将开发模式的配置放入webpack.dev.js中:

webpack.dev.js

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
42
43
44
45
46
47
48
49
50
51
52
53
const config = require('./webpack.common.js');
const {
merge
} = require('webpack-merge');

module.exports = merge(config, {
mode: 'development',
devtool: 'source-map',
output: {
publicPath: '',
},
devServer: {
open: true,
host: "localhost",
port: 8000,
hot: true,
historyApiFallback: {
index: '/index.html'
}
},
module: {
rules: [{
test: /\.css$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
esModule: false,
}
},
]
},
{
test: /\.less$/,
use: [
'vue-style-loader',
{
loader: 'css-loader',
options: {
esModule: false,
}
},
'less-loader'
]
},
]
},
optimization: {
runtimeChunk: 'single'
}

});

将生产模式的配置放入webpack.prod.js中:

webpack.prod.js

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const { merge } = require('webpack-merge');
const config = require('./webpack.common.js');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = merge(config, {
mode: 'production',
output: {
publicPath: '',
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css'
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/,
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: ['default', {
discardComments: {
removeAll: true
}
}],
},
canPrint: true
}),
],
module: {
rules: [{
test: /\.css$/,
sideEffects: true,
use: [{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../"
}
},
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
sideEffects: true,
use: [{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../"
}
},
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
});

使用 webpack-cli 脚手架初始化项目,需要安装 @webpack-cli/generators 依赖;
根据项目需要选择相应配置;

1
> npx webpack-cli init <project>

实现热重载

热重载能为开发提供极大的便利,不需要每次修改代码后手动编译,然后在浏览器中手动刷新页面来观察效果;同时每次只更新改动部分的代码,并且将编译的结果存在内存中;通过在内存中(而不是写入磁盘)编译和 serve 资源来提高性能。这里提供两种实现热重载的方式:

webpack-dev-serve
使用 webpack 官方提供的开发服务器实现热重载是最简便的方式,需要安装开发依赖webpack-dev-serve,然后只需要在webpack.dev.js配置文件中添加 devSever 项的配置即可;

webpack watch + live server
使用 webpack 的 watch 模式是一种替代方案,它会实时监听文件的变动,编辑保存后便开始编译,并将文件输出到 dist 文件夹下,然后开启 VS Code 的插件 live server 即可实现模块热替换。

附录

  1. package.json 属性说明
属性 含义
name 设置软件包的名称。
author 列出软件包的作者名称。
contributors 除作者外,该项目可以有一个或多个贡献者。 此属性是列出他们的数组。
bugs 链接到软件包的问题跟踪器,最常用的是 GitHub 的 issues 页面。
homepage 设置软件包的主页。
version 指定软件包的当前版本。x.x.x 分别表示主版本号、次版本号、补丁版本号仅修复缺陷的版本是补丁版本,引入向后兼容的更改的版本是次版本,具有重大更改的是主版本。
license 指定软件包的许可证。
keywords 此属性包含与软件包功能相关的关键字数组。
description 此属性包含了对软件包的简短描述。
repository 此属性指定了此程序包仓库所在的位置。
main 设置软件包的入口点。
private 如果设置为 true,则可以防止应用程序/软件包被意外发布到 npm 上。
scripts 可以定义一组可以运行的 node 脚本。
dependencies 设置作为依赖安装的 npm 软件包的列表。
devDependencies 设置作为开发依赖安装的 npm 软件包的列表。
browserslist 用于告知要支持哪些浏览器(及其版本)。 Babel、Autoprefixer 和其他工具会用到它,以将所需的 polyfill 和 fallback 添加到目标浏览器。
  1. 软件包版本说明符
符号 含义
~ 如果写入的是 〜0.13.0,则只更新补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
^ 如果写入的是 ^0.13.0,则要更新补丁版本和次版本:即 0.13.1、0.14.0、依此类推。
* 如果写入的是 *,则表示接受所有的更新,包括主版本升级。
> 接受高于指定版本的任何版本。
>= 接受等于或高于指定版本的任何版本。
<= 接受等于或低于指定版本的任何版本。
< 接受低于指定版本的任何版本。
无符号 仅接受指定的特定版本。
latest 使用可用的最新版本。
|| 使用范围组合

参考资料