webpack.docschina.org/blog/2020-1…
引言
2020年10月,webpack 发布了 webpack 5.0.0。作为前端工程化的核心能力,webpack团队考虑到开发者的学习成本,并没有做成重大的 API 升级(ღ( ´・ᴗ・)ღ), 对比了一下 webpack4,对一些核心的升级点做下记录并分享
`
Node.js Polyfills 不再引入
webpack 5 开始,更加专注于前端工程构建,不再自动填充 Node Api 垫片,提高 web 构建效率
webpack 4
import { crypto } from 'crypto';
console.log('node 模块测试');
这些 polyfill 比较庞大,浪费性能,webpack 本身也给出了警告,我们需要通过其他途径来优化(polyfill.io)
webpack 5
import { crypto } from 'crypto';
console.log('node webpack5 模块测试');
webpack 5 不再引入并编译失败,希望你为这些 node api, 配置 polyfill
如果你的项目中使用到了 Node 模块,我们可以通过如下配置来开启:
// webpack.config.js
module.exports = {
mode: 'production',
...
resolve: {
fallback: {
// 以 crypto 举例, 如果使用到某些 api,可通过如下配置来兼容
"crypto": require.resolve("crypto-browserify"),
// 完全没用到,不引入垫片,可通过如下配置来关闭
"crypto": false
}
}
}
支持资源模块编译
webpack5 开始,对各种资源文件进行编译能力内置,jpg/gif/txt/.. 等资源文件不再需要 通过file-loader, url-loader 等资源处理 Loader 进行拓展编译了。
现在 webpack 天然支持如下模块webpack 天生支持如下模块类型`
- ECMAScript 模块
- CommonJS 模块
- AMD 模块
- Assets
- WebAssembly 模块
此前,我们通过 url-loader 来对部分小文件进行优化( < 1024 * 5 转换 base64)
现在对于公共资源类的规则配置,可通过下面几种方式来完成:
// webpack.config.js
// 方式一
module.exports = {
mode: 'production',
...
// 写法一
module: {
generator: {
'asset': {},
'asset/inline': {},
'asset/resource': {},
'asset/source': {},
}
}
// 写法二
module: {
parser: {
'asset': {},
'asset/inline': {},
'asset/resource': {},
'asset/source': {},
}
}
// 写法三
module: {
rules: [
{
test: /\.jpg$/,
type: 'asset/resource',
parser: {},
},
{
test: /\.png$/,
type: 'asset/inline',
parser: {},
},
{
test: /\.txt$/,
type: 'asset/source',
parser: {},
}
]
}
}
webpack 5 内部各模块对应方式如下 asset/inline => url-loader, asset/source => raw-loader, asset/resource => file-loader。
更详情的规则可以点击这里
持久化编译缓存机制更新
webpack 5 中,新更新了缓存策略,通过缓存机制的使用,可以大幅提高我们开发、编译的构建速度。
webpack 4
webpack < 5 中,我们通常会使用 babel 插件各类配置或者 manifest.json 文件索引来优化构建速度
webpack 5
添加测试代码:
// webpack.config.js
module.exports = {
mode: "production",
...
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: [
{
loader: "babel-loader",
options: {
presets: [
"@babel/preset-react"
]
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
],
...
}
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app.jsx';
ReactDOM.render(<App />, document.getElementById('root'));
//app.jsx
import React from 'react';
import { fnA1 } from './moduleA';
class App extends React.Component {
componentDidMount() {
fnA1();
}
render() {
return <div>hello react</div>;
}
}
export default App;
webpack --config webpack.config.js
两次构建的输出如下:
第一次
第二次
两次构建时间接近 3000ms,接下来开启 webpack5 长缓存机制
// webpack.config.js
module.exports = {
mode: "production",
...
cache: {
type: "fileSystem"
}
...
}
第二次构建,可以看到速度提高了6倍。
可以在 node_modules/.cache/webpack 中找到缓存文件, 可通过 cacheDirectory 配置来修改缓存路径。
实际使用中,webpack 中提供fileSystem | memory
两种方式, memory 会将构建产物缓存至系统内存中提高构建速度,这种模式只能在 development
模式中使用。
默认情况下,webpack 5 在生产模式的构建中,会使用 文件 + 内容 进行 hash 值计算,在开发模式则使用时间戳进行缓存机制处理,配合长期缓存算法机制,webpack5 还更新了moduleIds 、chunkIds 的生成机制【看下面】。
Optimization 之 moduleIds 、chunkIds的变更
【Tip】 module 和 chunk 可以简单理解为一个是编译前的文件,一个是编译后的产物,一个文件对应一个 module, 一个代码块对应一个 chunk, 不同 chunk 间相互引用。通常,运行在浏览器上的脚本又称之为 bundle。
webpack 5 之前,异步模块以数字的形式递增的方式输出,这样缓存机制在某些情况下就会失效
webpack4
webpack4 增加代码
// demo/index.js
import { comm1, comm2 } from './d';
(async () => {
await import('./a');
await import('./b');
await import('./c');
})()
console.log(comm1, comm2);
// webpack.config.js
module.exports = {
mode: "production",
...
optimization: {
splitChunks: {
chunks: 'all',
minSize: 500,
minChunks: 1,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
}
},
}
编译输出如下:
webpack5
同样的文件,在 webpack 5 中,会输出为短数字 id。
webpack 5,会在 production
模式下,默认开启 chunkIds: "deterministic"
模式,将文件内容进行内容级别的缓存。
更强大的 tree-shaking
webpack 还有一个重要的优化算法就是 tree-shaking, webpack 5 相比 webpack4,对 tree-shaking 算法进行了更进一步的增强:支持嵌套的模块的 tree-shaking 及 内部模块 tree-shaking
webpack 4
增加代码
// a.js
function a () {
console.log('a')
}
function b () {
console.log('b')
}
export default {
a, b
}
// index.js
import a from './a'
console.log(a.a());
console.log('hello world');
webpack 4 构建输出:
webpack 4 查找 a 模块中存在 a.a() 的引入,于是将 a 模块进行引入。
webpack 5
同样的引入方式及使用,webpack 5 则更加智能,会对多层依赖模块进行优化:
可以发现,b 被正确的剔除了。
模块联邦
除了上述几个比较关键的配置或优化变更外,webpack 对于远程异步加载 bundle 新增了模块联邦的加载机制,它允许多个 webpack 构建产物一起工作。允许从指定的远程构建中导入,并以最小的限制来使用远程的 js bundle。
这种加载机制,非常适用于微前端的加载方式,因为微前端本身的机制也是跨域加载一段 js 脚本来插入第三方模块视图。
后续会更新联邦模块的测试 demo。
总结
总体来看,webpack 5 为了降低开发者的学习成本,尽可能小的优化了核心算法及模块优化机制,让 webpack 功能更加强大,确不增加我们的学习成本,这使得我们老项目迁移升级 webpack5 变的更加简单。关键的更新点可以总结如下:
- 持久性缓存来提高构建性能。
- 更加强大的 Tree Shaking 算法
- 编译能力的增加,更加关注于 web 构建的能力
- 更好的内容级 hash 算法改进长期缓存
- 面向现代化复杂业务的解决方案