2020 年真是多事之秋,疫情啊、牛市啊就这样措不及防地来了。。。
前端里也是,被社区推动着学习,之前不小心在 twitter 上刷到 React 17 发布,看完后幸好没什么大改动(hooks 就已经喝够一壶了)。接着掘金里狂刷 Vue 3.0 发布,大航海时代神马的醉了,不过幸好主技术栈不是它 😉。然后当一名老 webpack 配置工程师去官网查文档的时候,纳尼,webpack 5 发布了!
简译于官方文档,加入一些自己的理解(&吐槽),不保证原汁原味,建议查阅官网。
webpack 5 发布(2020-10-10)
距离 2018 年 2 月的 webpack 4 发布已有两年,老实说挺不容易的,因为 webpack 主要都是作者在维护。很多人都不喜欢 breaking changes,特别是 webpack 这种只要能用就行了就不会花时间改它,但是没有 breaking changes 就意味着没法儿进行大更新和优化。
扯了一阵犊子,也确实该优化下了,这启动速度,还有君不见很多神奇的 bug。
这个主要的版本修改了一些默认的配置,以及向未来看齐。
不得不说,很多发布文章都一样,又臭又长,看完就跟没看一样,等着哪天用着不对劲了才发现这个东东改了。。
这次发布主要内容:
- 打包优化,基于持久缓存 (Persistent Caching)。没用过
cache-loader
直接略过。 - 长期缓存优化,基于更好的算法。Long Term Cacheing,听名词可能不太懂,主要是指打包出来的文件名称是一致的,这样利于浏览器的缓存机制,比如一个单独提取出来的第三方库,每次打包出来名称都一样的话用户只需要第一次访问下载就行了。
- 打包后的文件大小优化,基于更好的 Tree Shaking,代码生成的算法也优化了。
- 提升 web 平台的兼容性。
- 清理内部结构。略过
- 一些 breaking changes 和未来的新功能。
接下来喜闻乐见的这个版本主要改了啥。用到的要改的 😫
主要改动 Major Changes
移除的一些东西:
- v4 所有的弃用项都被移除了。
- 新的弃用项。包括
require.include
,没用过略过。 - Node.js Polyfills 被移除了。比如
crypto
模块等
长期缓存 Long Term Cacheing,新的默认算法:
module.exports = {
optimization: {
// v4
- chunkIds: "size",
- moduleIds: "size",
- mangleExports: "size",
// v5
+ chunkIds: "deterministic",
+ moduleIds: "deterministic",
+ mangleExports: "deterministic",
},
};
v4 版本一般都是
contenthash
+HashedModuleIdsPlugin
来做长期缓存。
真正的内容哈希。webpack 5 使用真正的文件内容生成哈希,以前只基于内部结构。因此当注释或变量名称改变时会影响长期缓存。压缩了的话没啥变化,甭管。
开发环境新的默认 Chunk ID 命名算法,主要跟动态引入的模块生成后的文件名相关,以前是生成的数字,现在基于其内容,import(/* webpackChunkName: "name" */ 'module')
这种。(生产环境建议继续这样写)
Module Federation
重点!这应该算的上 webpack 5 最大的特性了,让不同项目里的模块能一起工作,说个微前端就都懂了 😂
简单的例子:
// 项目 app_remote 的配置
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app_remote',
library: { type: 'var', name: 'app_remote' },
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button.jsx',
},
shared: ['react', 'react-dom'],
}),
],
};
使用时,先引入 remoteEntry.js
,再定义插件,最后代码中使用:
<!-- index.html -->
<script src="http://localhost:8002/remoteEntry.js"></script>
// webpack.config.js
module.exports = {
plugins: [
new ModuleFederationPlugin({
name: 'app',
remotes: {
app_remote: 'app_remote',
// 或者
app_remote: 'app_remote@http://localhost:8002/remoteEntry.js',
},
shared: ['react', 'react-dom'],
}),
new HtmlWebpackPlugin({ template: './index.html' }),
],
};
import React from 'react';
// 从 app_remote 项目中引入的模块
const Button = React.lazy(() => import('app_remote/Button'));
export default function Page () {
return (
<div>
<h1>Page title</h1>
<React.Suspense fallback="Loading...">
<Button />
</React.Suspense>
</div>
);
}
功能确实强大,不过作为使用者,感觉可能会有许多问题,比如:
- webpack 打包模式不同难以调试。
app_remote
线上只是生产环境,而本地开发app
一般都是 development mode,如果说app_remote
也在本地克隆下来启动,但remotes
一多总不可能逐个启动吧。 - 第三方库版本问题?抑或者
remotes
可用性? - 组件使用者的体验。框架一般都有文档能查阅,这个上哪搞?给项目里的组件写文档能给烦死。
- 平时最多的是写业务,如果产品临时把这个项目的业务流程改了,涉及到共用的业务组件咋办。
唉~光是 react、vue 不同的特性就让微前端像是个梦了,何况跨平台乎。不过有这么多大佬推动着前端发展,还是被学习好了 😂
接着 webpack 的改动,一些 web 平台新特性:
- JSON modules 对齐规范,没有 named exports 又是几个意思。。。貌似之前就能直接导入,内置了
json-loader
import.meta
。模块的基本信息,目前只规范了个url
模块绝对地址- 资源模块新写法,
new URL('./assets/image.png', import.meta.url)
。原生的写法 - 原生 Worker 支持,包括
new Worker/SharedWorker/ navigator.serviceWorker.register
。webpack 会单独打包这些文件:new Worker(new URL('./worker.js', import.meta.url))
。也是原生写法,要浏览器支持 - URI 协议支持,包括
data:
、file://
、http(s)://
。比如:import x from 'data:text/javascript,export default 32'
。 - Async modules 支持。不会,略过
- 新的
external
类型,promise/import/script
。script
能通过<script>
标签加载,可以不用写到 html 里了。
Node.js 生态新特性。略过
一些开发体验的优化。target
可以指定多个且指定版本,比如:target: ['web', 'es2020']
;Stats 输出信息优化;Progress 进度信息优化,插件还加了个百分比选项;自动定义多个 runtime 在同个页面下时,之前会有冲突;自动定义 output.publicPath
如果没设置的话;可生成 ts 类型文件。
一些打包优化:
- tree-shaking。包括了嵌套模块,模块内部,以及 CommonJS 模块。
- 模块副作用分析。以前需要在
package.json
中手动定义sideEffects
,现在基于源代码自动标记这些模块。 - 优化每次打包
- Module Concatenation
- 代码生成优化
splitChunks.minSize
可指定不同类型
性能优化。文件系统的持久缓存、文件生成。这块不会翻,高级用户还是直接看文档好了 🙄
一直以来存在的问题。没碰到过,略过
实验性性功能。详情请看 experiments
。Top-Level-Await 了解一下,基本上是未来的规范:
import React from 'react';
// Stage 3 proposal
// 顶层作用域直接使用 await
const x = await import('x');
最小的 Node.js 版本 6 -> 10.13.0
。
一些配置的改动。只列举写常见的,如果启动出问题的话直接去官网查好了:
entry: {}
可以通过插件添加入口文件target
上文提过的支持数组和版本,默认 browerslist 如果有的话cache
新的 filesystem 类型output.filename
可以是个函数。顶一下,可以自定义生成的文件名称。之前在 webpack 4 文档里查到这个选项,开心地试了,but 并不能用,后来才在 issue 里找到这是 webpack 5 里的功能,吐槽下文档,caoptimization.moduleIds: 'deterministic'
添加的默认项module.rules
loaders
被删除了,用use
optimization.splitChunks.cacheGroups.vendors
重命名成defaultVendors
- etc...
loader 相关的改动。写过 loader 的可以关注下。
内部细节的改动。插件应用的顺序改了,依赖的 tapable 升级,分析模块依赖用的 ModuleGraph 和 ChunkGraph 改动了,最后顺带提下,文件监听系统重构改用 node 原生的 fs
了,不知道是好是坏啊。。。更多的看不动了 😱
做为一名老 webpack 配置工程师,基本上就到此为止了,看过源码的大佬们可以详查之。
次要改动 Minor Changes
可在乎~(太多了...很多都是和常用配置没啥关系的)
最后总结一下,看完过会儿就等于没看一样。
现在基本上都是用脚手架了,诸如 create-react-app
这种,有大佬在调 webpack 配置,省得翻文档了。2020 年了也衍生了几个基于 esm 的打包工具,比如 snowpack
、esbuild
,据说打包速度贼快,阔以试试。
参考链接
- Webpack 5 release (2020-10-10)
- module-federation | webpack
- Webpack 5 Module Federation: A game-changer in JavaScript architecture
- module-federation/module-federation-examples - Github
原文链接(博客小站引流): www.ningtaostudy.cn/articles/bi…