版本
一、Webpack 4.x
二、Webpack 5.x
2.1 启动命令
2.2 持久化缓存
Webpack5 新增cache
,用于缓存生成的webpack模块和chunk,改善构建速度。Webpack5追踪了每个模块的依赖,并创建了文件系统快照。此快照会与真实文件系统进行比较,当检测到差异时,将触发对应模块的重新构建。
2.3 资源模块
资源模块 是一种模块类型,它允许使用资源文件(字体、图标、图片等)而无需配置额外的**loader
**
-
raw-loader
=>asset/source
导出资源的源代码 -
file-loader
=>asset/resource
发送一个单独的文件并导出URL
-
url-loader
=>asset/inline
导出一个资源的Data URI
-
asset
=> 在导出一个Data URI
和发送一个单独的文件之间自动选择。之前通过使用url-loader
,并且配置资源体积限制实现
2.4 moduleIds 和 chunkIds 的优化
**module
为每一个文件都可以看成一个 module
。chunk
**为 Webpack
打包最终生成的代码块,代码块会生成文件,一个文件对应一个 chunk
。在生产模式下,默认启用chunkIds:"deterministic"
,moduleIds:"deterministic"
,此算法采用确定性的方式将短数字 ID
(3个或4个字符)短 **hash
**值分配给 **modules
**和 chunks
。
对于moduleIds
而言:
- 开发模式下:
moduleId
是相对于根目录的相对路径。这种方式的好处就是容易让人立即,一眼就看出来是哪个文件。缺点就是moduleId
太长,增加了文件体积,有时候moduleId
还可能是一个敏感文件,容易暴露信息 - 生产模式下:
对于chunkIds
而言:
-
开发模式下:
-
生产模式下:
webpack.config.js
通过optimization
配置moduleIds
与chunkIds
const Path = require("path");
module.exports = {
mode: "development",
entry: Path.resolve(__dirname, "src", "index.js"),
cache: {
type: "filesystem",
cacheDirectory: Path.resolve(__dirname, "node_modules/.cache/webpack"),
},
output: {
filename: "index.js",
path: Path.resolve(__dirname, "build"),
},
optimization: {
moduleIds: "deterministic",
chunkIds: "deterministic",
},
};
2.5 更智能的 Tree Shaking
Webpack4
本身的 Tree Shaking
比较简单,主要是找一个 import
进来的变量是否在这个模块中出现过。Webpack5
可以进行根据作用域之间的关系来进行优化。
2.6 nodeJs 的 Polyfill 脚本移除
Webpack4
带了许多 Node.js
核心模块的 polyfill
,一旦模块中使用了任何核心模块(如crypto
),这些模块就会被自动启用。Webpack
不再自动引入这些 polyfill
。
理解: 比如说在webpack5
中使用crypto-js
依赖库,crypto-js
其中用到了 **Node
**中的crypto
模块,那么我们现在要手动通过resolve.fallback
来引入这个模块
resolve:{
fallback:{
crypto: require.resolve('crypto-browserify'),
}
},
2.7 模块联邦
模块联邦(Module Federation
) 的动机是为了不同开发小组间共同开发一个或者多个应用。应用将被划分为更小的应用块。一个应用块,可以是比如头部导航或者侧边栏的前端组件,也可以是数据获取逻辑的逻辑组件,每个应用块由不同的组开发。应用或者应用块共享其他应用块或者库。
模块联邦(Module Federation
) ,每个应用块都是一个独立的构建,这些构建将编译为容器。容器可以被其他应用或者其他容器应用。一个被引用的容器被称为 remote
,引用者被称为 host
,**remote
**暴露模块给 host
, **host
**则可以使用这些暴露的模块,这些模块被称为 **remote
**模块。
配置参数
-
name
: 必传值,即输出的模块名,被远程引用时路径为${name}/${expose}
-
library
: 声明全局变量的方式,name
为umd
的name
-
filename
: 构建输出的文件名 -
remotes
: 远程引用的应用名及其别名的映射,使用时以 **key
**值作为name
-
exposes
: 被远程引用时可暴露的资源路径及其别名。注意: 该项目作为remote
时,通过exposes
暴露模块 -
shared
: 与其他应用之间可以共享的第三方依赖,使你的代码中不用重复加载同一份依赖。注意: 如果该项目作为remote
,那么shared
才有意义,配置才有效;项目只作为host
时配置shared
没有任何意义。
具体实现
-
项目一: 作为
remote
被其他项目引用,作为host
引用其他项目-
module1/webpack.config.js
const Path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MFP = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: Path.resolve(__dirname, "src", "index.js"),
output: {
filename: "index.js",
path: Path.resolve(__dirname, "build"),
},
devServer: {
port: 8001,
},
plugins: [
new HtmlWebpackPlugin({
template: Path.resolve(__dirname, "index.html"),
}),
new MFP({
name: "module1",
filename: "module1Entry.js",
remotes:{
module2:'module2@http://localhost:8002/module2Entry.js'
},
exposes: {
"./module": "./src/module.js",
},
shared: {
lodash: { singleton: true },
},
}),
],
}; -
module1/src/index.js
import('./bootstrap');
-
module1/src/bootstrap.js
import HostModule from 'module2/module'
console.log(HostModule)
function entry(){
console.log('我是 module1 bootstrap');
}
entry(); -
module1/src/module.js
import {concat} from 'lodash';
export default {
name: "module1 name",
age: "module1 age",
concat,
};
-
-
项目二: 作为
remote
被其他项目引用,作为host
引用其他项目-
module2/webpack.config.js
const Path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MFP = require("webpack/lib/container/ModuleFederationPlugin");
module.exports = {
mode: "development",
entry: Path.resolve(__dirname, "src", "index.js"),
output: {
filename: "index.js",
path: Path.resolve(__dirname, "build"),
},
devServer: {
port: 8002,
},
plugins: [
new HtmlWebpackPlugin({
template: Path.resolve(__dirname, "index.html"),
}),
new MFP({
name: "module2",
filename: "module2Entry.js",
remotes:{
module1:'module1@http://localhost:8001/module1Entry.js'
},
exposes: {
"./module": "./src/module.js",
},
shared: {
lodash: { singleton: true },
},
}),
],
}; -
module2/src/index.js
import('./bootstrap');
-
module2/src/bootstrap.js
import HostModule from 'module1/module'
console.log(HostModule)
function entry(){
console.log('我是 module2 bootstrap');
}
entry(); -
module2/src/module.js
import { concat } from 'lodash';
export default {
name: "module2 name",
age: "module2 age",
concat,
};
-
2.8 按需编译
Webpack 5.17.0
之后引入实验特性 lazyCompilation
,用于实现 entry
或异步引用模块的按需编译,这是一个非常实用的新特性!
试想一个场景,你的项目中有一个入口(entry
)文件及若干按路由划分的异步模块,Webpack
启动后会立即将这些入口与异步模块全部一次性构建好 —— 即使页面启动后实际上只是访问了其中一两个异步模块, 这些花在异步模块构建的时间着实是一种浪费!lazyCompilation
的出现正是为了解决这一问题。用法很简单:
// webpack.config.js
module.exports = {
// ...
experiments: {
lazyCompilation: true,
},
};
启动 lazyCompilation
后,代码中通过异步引用语句如 import('./xxx')
导入的模块(以及未被访问到的 entry
)都不会被立即编译,而是直到页面正式请求该模块资源(例如切换到该路由)时才开始构建,效果与 Vite
相似,能够极大提升冷启速度。
此外,lazyCompilation
支持如下参数:
-
backend
: 设置后端服务信息,一般保持默认值即可; -
entries
:设置是否对entry
启动按需编译特性; -
imports
:设置是否对异步模块启动按需编译特性; -
test
:支持正则表达式,用于声明对那些异步模块启动按需编译特性。