# webpack5 新特性
# 自动删除 Node.js Polyfills
剔除 npm 包里面针对 Node.js 模块自动引用的 Polyfills
v4 编译引入 npm 包,有些 npm 包里面包含针对 nodejs 的 polyfills,实际前端浏览器是不需要的
例如:
// index.js
import CryptoJS from 'crypto-js'
const md5Password = CryptoJS.MD5('123123')
console.log(md5Password)
v4 引入 crypto-js 模块会自动引入 polyfill: crypto-browserify, 但部分代码是不需要的,v5 默认会自动剔除
v5 编译中,会出现 polyfill 添加提示,如果不需要 node polyfille,按照提示 alias 设置为 false 即可
// webpack.config.js
resolve: {
// 1.不需要node polyfilss
alias: {
crypto: false
},
// 2.手动添加polyfills
// fallback: {
// "crypto": require.resolve('crypto-browserify')
// }
}
# 长期缓存优化
以前 v4 是根据代码的结构生成 chunkhash,现在 v5 根据完全内容生成 chunkhash,比如改了内容的注释或者变量则不会引起 chunkhash 的变化,让浏览器继续使用缓存
- moduleId 改为根据上下文模块路径计算,chunkId 根据 chunk 内容计算
- 为 module,chunk 分配确定的(3 或 5 位)数字 ID,这是包大小和长期缓存之间的一种权衡
# 持久化缓存
- 第一次构建是一次全量构建,它会利用磁盘模块缓存(以空间换时间),使得后续的构建从中获利。
- 后续构建具体流程是:读取磁盘缓存 -> 校验模块 -> 解封模块内容。
v5 默认情况,缓存配置是 memory,修改设置为 filesystem, 将缓存写入硬盘
// webpack.config.js
module.exports = {
cache: {
// 1. 将缓存类型设置为文件系统
type: 'filesystem', // 默认是memory,filesystem为磁盘存储
// 2. 将缓存文件夹命名为 .temp_cache,
// 默认路径是 node_modules/.cache/webpack
cacheDirectory: path.resolve(__dirname, '.temp_cache'),
buildDependencies: {
// 当配置修改时,缓存失效
config: [__filename]
}
}
}
# SplitChunk
// webpack4
minSize: 30000;
// webpack5
minSize: {
javascript: 30000,
style: 50000,
}
# Output
webpack 4 默认只能输出 ES5 代码
webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.
如:output.ecmaVersion: 2015
# 模板联邦
跨项目间的 chunk 可以相互共享
- UMD 模块
<script src="https://unkpg.com/lodash.js"></script>
- 微前端:多个项目共存于一个页面,有点类似 iframe,共享的对象是项目级的,页面级的 子应用间的 chunk 以及对象可通过全局事件共享,但是公共包在项目安置以及打包编译很难放
子应用独立打包,模块解耦了,但公共的依赖不易维护处理 整体应用一起打包,能解决公共依赖;但庞大的多个项目又使打包变慢,后续也不好扩展
- v5 的模块共享
这个方案是直接将一个应用的 bundle,应用于另一个应用,动态分发 runtime 子模块给其他应用。 模块联邦的使用方式如下:
module.exports = {
// other webpack configs...
plugins: [
new ModuleFederationPlugin({
// 1. name 当前应用名称,需要全局唯一
name: 'app_one_remote',
// 2. remotes 可以将其他项目的 name 映射到当前项目中
remotes: {
app_two: 'app_two_remote',
app_three: 'app_three_remote'
},
// 3. exposes 表示导出的模块,只有在此申明的模块才可以作为远程依赖被使用
exposes: {
AppContainer: './src/App'
},
// 4. shared可以让远程加载的模块对应依赖改为使用本地项目的 React或ReactDOM。
shared: ['react', 'react-dom', 'react-router-dom']
}),
new HtmlWebpackPlugin({
template: './public/index.html',
chunks: ['main']
})
]
}
比如设置了 remotes: { app_two: "app_two_remote" },在代码中就可以直接利用以下方式直接从对方应用调用模块
import { Search } from 'app_two/Search'
// app_two/Search来自于app_two 的配置:
// app_two的webpack 配置
export default {
plugins: [
new ModuleFederationPlugin({
name: 'app_two',
library: { type: 'var', name: 'app_two' },
filename: 'remoteEntry.js',
exposes: {
Search: './src/Search'
},
shared: ['react', 'react-dom']
})
]
}
正是因为 Search 在 exposes 被导出,我们因此可以使用 [name]/[exposes_name] 这个模块,这个模块对于被引用应用来说是一个本地模块。
# 构建优化
更好的 Tree Shaking
v4 有些场景是不能将无用代码剔除的
- 对于模块引入嵌套场景,如下在生产环境中, inner 模块暴露的 b 会被删除
// inner.js
export const a = 1
export const b = 2
// module.js
import * as inner from './inner'
export { inner }
// user.js
import * as module from './module'
console.log(module.inner.a)
- 只有 test 方法使用了 someting 。最终可以实现标记更多没有使用的导出项
import { something } from './something'
function usingSomething() {
return something
}
export function test() {
return usingSomething()
}
当设置了"sideEffects": false 时,一旦发现 test 方法没有使用,不但删除 test,还会删除"./something"
- Commondjs。现在 Webpack 不仅仅支持 ES module 的 tree Shaking,commonjs 规范的模块开始支持了
# Chunk 和模块 ID
添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。
chunkIds: "deterministic",
moduleIds: "deterministic"
# Named Chunk ID
你可以不用使用 import(/_ webpackChunkName: "name" _/ "module") 在开发环境来为 chunk 命名,生产环境还是有必要的
webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了