介绍webpck5
距离webpack5推出也快有一年多的时间,现在还处在测试版本的阶段,目前已经可以安装使用了
安装方式
1 2 3 4 | ``` npm install webpack@next webpack-cli -D yarn add webpack@next webpack-cli -D ``` |
HTML插件兼容HTML
- 需要将
html-webpack-plugin 升级到4.3.0版本
环境要求
- 最新的官方文档要求node至少在10.13.0版本以上
新特性介绍
- 持久化缓存
- moduleIds & chunkIds的优化
- 更智能的tree shaking
- nodeJs的polyfill脚本被移除
- 支持生成e6/es2015的代码
- SplitChunk和模块大小
- Module Federation
使用体验
- 经过上手使用,webpack5打包体积大小,持续编译速度都有很不错的提升,对webpack4兼容也很平缓,Module Federation也对项目中如何使用微型前端应用提供一种解决方案。如果升级过程中有遇不兼容的情况,可以去webpack5变更日志上查阅。
持久化缓存
- 在webpack5之前,可以使用
cache-loader 将编译结构写入硬盘缓存,还可以使用babel-loader ,设置option.cacheDirectory 将babel-loader 编译的结果写进磁盘。 - 在webpack5中,默认开启缓存,缓存默认是在内存里。可以对cache进行设置
1 2 3 4 5 6 7 8 9 10 11 12 13 | module.export={ type:'filesystem', // 'memory' | 'filesystem' cacheDirectory: 'node_modules/.cache/webpack', // 默认将缓存存储在 node_modules/.cache/webpack // 缓存依赖,当缓存依赖修改时,缓存失效 buildDependencies:{ // 将你的配置添加依赖,更改配置时,使得缓存失效 config: [__filename] } } |
-
默认情况下webpack 会假定其所处的 node_modules 目录仅由包管理器修改,将会跳过hash和时间戳处理,出于性能考虑,仅使用package的名称和版本。
-
缓存淘汰策略设计:文件缓存存储在 node_modules/.cache/webpack,对于一个缓存集合,最大限度应该不超过 5 个缓存内容,最大累积资源占用不超过 500 MB,当逼近或超过 500MB 的阈值时,优先删除最老的缓存内容。同时,也设计了缓存的有效时长为 2 个星期。
moduleIds & chunkIds的优化
chunk 和 module
- chunk:webpack打包最终生成的单独文件块,最终生成的单独文件,一个文件对应一个chunk。
- module:每一个源码 js 文件其实都可以看成一个 module。
chunkId的缺点
- webpack5改进了
moduleIds 和chunkIds 的确定。在webpack5之前,没有从entry 打包的chunk文件,都会以1,2,3...的文件命名方式输出。(文件名称后的hash值是用chunkhash生成的)
- 这样会造成一个后果是,当删除或者暂时不使用1.js这个文件后,那么2.js->1.js,3.js->2.js,这样就会造成原本线上的2.js请求时会造成缓存失效.
- 在webpack5之前也是可以通过
webpackChunkName 来解决命名问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ... <Switch> <Route key='/' exact path='/' component={ Loadable({ loader: () => import(/* webpackChunkName: "home" */ './home'), loading: (<div>loadding</div>) }) }/> <Route key='/page1' exact path='/page1' component={ Loadable({ loader: () => import(/* webpackChunkName: "page1" */'./page1'), loading: () => (<div>loadding</div>) }) } /> <Route key='/page2' exact path='/page2' component={ Loadable({ loader: () => import(/* webpackChunkName: "page2" */'./page2'), loading: () => (<div>loadding</div>) }) } /> </Switch> .... |
- 这样似乎解决了缓存失效的问题,但我们打开编译后的home.js 会发现还是存有chunkId。如果删除掉home这个菜单,page1,page2打包后的chunkId还是会发生变化,
- page1.js打包的文件
- 删除完home.js后page1.js打包的文件
- 即使page1.js没有做任何修改,但是由于home.js删除导致的chunkId的变化,所以page1.js的chunkhashi还是会发生变化,缓存失效的问题依旧存在。
webapck5 的改进
- 采用新的算法,在生产模式下,默认启用这些功能
chunkIds: "deterministic", moduleIds: "deterministic" 。 - 此算法采用确定性的方式将短数字 ID(3 或 4 个字符)分配给 modules 和 chunks。这是基于 bundle 大小和长效缓存间的折中方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | optimization.moduleIds: 可选值: 1. false 告诉webpack不应使用任何内置算法,通过插件提供自定义算法 2.natural 按使用顺序的数字ID。 3.named 方便调试的高可读性id 4.deterministic 根据模块名称生成简短的hash值 5.size 根据模块大小生成的数字id optimization.chunkIds: 可选值: 1. false 告诉webpack不应使用任何内置算法,通过插件提供自定义算法 2.natural 按使用顺序的数字ID。 3.named 方便调试的高可读性id 4.deterministic 根据模块名称生成简短的hash值 5.size 根据请求到的初始资源size计算的id 6..total-size:根据请求到的解析资源size计算的id |
更智能的tree shaking
- 开启tree shaking的条件跟webpack4一样,必须使用ES6模块化,开启production环境
- 例子
1 2 3 4 5 6 7 8 9 10 11 12 | // a.js export const a =22; export const b = 33; // b.js import * as a from './a.js' export {a}; // index.js import * as b from './b.js' console.log(b.a.a) // 输出22 |
- webpack4:打包时,会把
const b =33 也打包进去
- webpack5:打包的出来的代码则更精简
nodeJs的polyfill脚本被移除
- webpack <= 4 附带了许多 Node.js 核心模块的 polyfil,一旦模块中使用了任何核心模块(即 ”crypto“ 模块),这些模块就会被自动启用。
- 虽然这使得为 Node.js 编写模块变得简单,但它会将超大的 polyfill 添加到 package 中。在许多情况下,这些 polyfill 并非必要。
1 2 | import CryptoJS from 'crypto-js'; console.log(CryptoJS.MD5('123123')); |
- webpack4 :
- webpack5:
支持生成e6/es2015的代码
- webpack5 在output中新添加了ecmaVersion,设置
output: { ecmaVersion: 6 } 就可以使用,在webpack中默认编译生成的代码对应的是es5版本。 - 两种设置方式:
5 =< ecmaVersion <= 11 2009 =< ecmaVersion <= 2020
SplitChunk和模块大小
- 模块现在能够以更好的方式表示大小,而不是显示单个数字和不同类型的大小。
- 默认情况下,只能处理 javascript 的大小,但是你现在可以传递多个值来管理它们:
1 2 3 4 5 6 7 8 | optimization{ splitChunks{ minSize: { javascript: 30000, style: 50000, } } } |
Module Federation
- Module Federation 使 JavaScript 应用得以从另一个 JavaScript 应用中动态地加载代码 —— 同时共享依赖。让代码直接在项目间利用 CDN 直接共享,不再需要本地安装 Npm 包、构建再发布了!
- 动态加载 可以允许代码在运行时按需加载另一个应用的代码。
- 例子:在app1中引用app2中暴露出来的一个按钮组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // app1 - webpack.config.js const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); module.exports = { //.... plugins: [ new ModuleFederationPlugin({ name: "app1", // 应用名称 唯一 library: { type: "var", name: "app1" }, remotes: { // 需要引用远程应用,与app2中library的name字段保持一致 app2: "app2", }, shared: ["react", "react-dom"], // 先判断存不再存这个包,如果不存在就使用app2里的依赖 }), ], }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // app2 - webpack.config.js const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); module.exports = { //.... plugins: [ new ModuleFederationPlugin({ name: "app2", // 应用名称 唯一 library: { type: "var", name: "app2" }, // UMD标准导出,和name保持一致即可。 filename: "remoteEntry.js", 暴露出去的chunkname exposes: { 要暴露出去的模块 "./Button": "./src/Button", }, shared: ["react", "react-dom"], 与app1共享的依赖,如果app1中有,则会优先使用app1中的依赖。(注:被app1引用)时候会按照app1的/package.json中的版本要求来加载 }), ], }; |
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 | // app1/index.html // 引入app2暴露的文件 ... <head> <srcipt src="//i1.wp.com/localhost:3002/remoteEntry.js"></script> </head> ... app1/app.js import AppTwoButton from 'app/Button'; const App = ()=>{ return ( <AppTwoButton/> ) } app1/bootstrap.js ReactDOM.render(<App />, document.getElementById("root")); app1/bootstrap.js import("./bootstrap"); |
- 从加载流程看懂Module Federation
- 首先加载的是 app2/remoteEntry.js ,因为是在html中加载的,通过观remoteEntry.js 可以看出,webpakc是将app2定义在了全局变量里面了,这样app1就能够加载app2里面的模块了。
- 加载app1的打包后的main.js,进入到bootstrap.js寻找依赖前置,分析
- 然后在加载main.js里的react.js以及react-dom,react依赖的prop-types.js
- 接着经过remote的时候,发现依赖app2的Button组件
- bootstrap.js所有依赖都加载完之后,异步加载完成,执行then逻辑,启动应用
总结
- Module Federation给微前端,代码细分,共享依赖,按需加载提供新的思路,而且能做到运行时加载。但缺点公用全局变量和全局style,以及将模块暴露在全局变量下不够优雅。
- webpack5还在不断合并代码的过程中,目前版本bate22,webpack5正式版出来后,相信Module Federation功能还会继续优化。总之升级webpack5之后,构建速度和打包体积都有不错的提升。
参考资料
- 1.三大应用场景调研,Webpack 新功能 Module Federation 深入解析:https://developer.aliyun.com/article/755252
- 2.Webpack5 上手测评 https://juejin.im/post/5ecd05a1f265da76c4243fe6