产物优化
到这里终于要进入整体了,但是前面的操作都不是无用的,我们应该每个项目都通过上面的方式先分析一下,需要优化的部分。根据我们上面的分析和发现,我们可以先将一些常用的比较大的库,比如 react 、 react-dom 、 @antv/f2,使用 cdn 的方式引入。
图片资源压缩
这是最有效减小产物包大小的一步,却是被很多人忽略的一步,很多朋友都是直接下载了 UI 提供的切图,并没有对图片进行处理,其实适当的对图片进行压缩其实不会影响显示效果的。比如这里简单的处理一下就能从减少4MB的大小。压缩工具有很多比如 TinyPNG 或者 pngquant。
配置 externals
(配置 externals 还能减小编译消耗,使你项目的编译时间更短。)
export default { externals: { 'react': 'window.React', 'react-dom': 'window.ReactDOM', '@antv/f2': 'window.F2', }, scripts: [ 'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js', 'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js', 'https://gw.alipayobjects.com/os/antv/assets/f2/3.4.2/f2.min.js', ], }
我们通过上述的方法,再次查看请求,发现比之前多请求了三个远程的 js 文件,但是总耗时缺少了几秒,只有 18.55s。
查看代码分析页面,我们发现 react 和 react-dom 已经被从产物包里面移除了,但是 @antv/f2 却还在包里面。这就导致了我们引入了两次的 @antv/f2 的库,虽然不会冲突,项目也可以正常运行,但是白白消耗的时间和性能,却是无法接受的。于是我全局搜索了一下项目中对 f2 的引用。
发现项目中对f2 的引用,使用的是 import F2 from '@antv/f2/lib/index-all';
,可能你不太理解为什么要这么引用,因为这个情况可能出现在其他的你不熟悉的库里面,我们就不对项目代码做任何的修改,这时我们只需要修改一下我们的配置文件就可以。
export default { externals: { 'react': 'window.React', 'react-dom': 'window.ReactDOM', - '@antv/f2': 'window.F2', + '@antv/f2/lib/index-all': 'window.F2', }, }
这种方式引入并不是越多越好的,浏览器对同一个 hostname 发起的请求数量是有限制的,特别在安卓的 webview 中,限制的更加明显,因此可以通过观察首次发起的请求数量,来酌情处理。当然,土豪组织也可以通过增加不同的 cdn 主机来解除限制。
选用可替代的依赖库
包名 | Stat Size | Parsed Size | Gzip Size |
monent | 659.12KB | 367/65KB | 77.78KB |
我们发现 monent 库比较大,刚好社区有一个 dayjs ,api 和用法都和 monent 一致,但是包大小却非常小。
// package.json "dependencies": { "@alitajs/dform": "^1.5.3", "@alitajs/list-view": "^0.2.4", "@antv/f2": "^3.7.0-alpha.1", "alita": "2.5.5", "classnames": "^2.2.6", "copy-to-clipboard": "^3.3.1", "crypto-js": "^4.0.0", + "dayjs": "^1.8.29" - "monent": "^2.24.0" },
这里我们使用一种不推荐但可能更高效的方式来修改,全局替换。然后有修改的页面再打开看一下,如果有 api 报错,再去添加 dayjs 相关的插件。
查看代码分析页面,发现经过我们上面的简单配置,包已经变小了。
包名 | Stat Size | Parsed Size | Gzip Size |
旧的 umi.js | 6.77MB | 2.75MB | 905.54KB |
新的 umi.js | 5.41MB | 2.16MB | 718.7KB |
如果你发现某一个较大的依赖包,你没有在项目中使用,那就是第三方依赖库中有使用到了,可以简单的翻一下 yarn.lock 文件,就可以发现它们的依赖关系。如果你有更好更小的替代库,可以直接给这些需要优化的第三方库提 Issues 或者 PR ,比如我们之前发现 @alitajs/dform 中使用了 monent,于是我们提交了 PR ,现在在最新的版本中,就没有使用 monent 了。
调整 splitChunks 策略
增加配置,如果你对 webpack 的配置不熟悉也不要紧,这里你只需要关注 cacheGroups 中的配置,尤其是每一项里面的 test ,比如这里的 /[\\/]node_modules[\\/]/
表示将 node_modules 中的所有的库拆分到一个新的js文件中,文件名称为 vendors.js 。
export default { chainWebpack(config:any) { config.merge({ optimization: { splitChunks: { chunks: 'all', automaticNameDelimiter: '.', name: true, minSize: 30000, minChunks: 1, cacheGroups: { vendors: { name: 'vendors', chunks: 'all', test: /[\\/]node_modules[\\/]/, priority: -12, }, }, }, }, }); } }
增加文件加载顺序声明 chunks ,因为我们增加了一个 js 文件,这是我们就要告诉项目,应该先加载哪个文件,如果你有增加其他的拆分文件,记得也要同步添加这个配置。
export default { chunks: ['vendors', 'umi'], }
查看分析页面,我们可以看到两个 js 文件,分别由 node_modules 文件夹的内容和 src 文件夹的内容组成。
包名 | Stat Size | Parsed Size | Gzip Size |
umi.js | 1.45MB | 760.03KB | 230.05KB |
vendors.js | 3.96MB | 1.42MB | 454.66KB |
拆分前的 umi.js | 5.41MB | 2.16MB | 718.7KB |
移除非必要的请求
从我们的请求日志中,可以发现一个外部js的引用,它不大但是却很耗时间。看了一下是高德地图需要引入的全局js文件。
我们通过浏览器直接访问 https://webapi.amap.com/maps?v=1.4.15&key=demokey
。可以得到一个js文件,
我们将这个js下载到项目中,然后在 global.ts 中引入 import '@/utils/amap.js';
,删除项目中对高德地图的引入,比如这里是在 src/pages/document.ejs 中删除对应的 script。
虽然将文件打包到项目中,会使得文件变大。可能有些朋友看到这里会比较疑惑,我们上面一直在努力的减小我们的文件大小,为什么这里又选择增加了文件大小?其实如何做产物优化这回事,并没有什么最优解,具体问题具体分析,文件太大影响访问数据,就减小单文件大小,文件太多,引起访问受限,就合并文件数量,提供外链的cdn服务有问题,就将文件下载到本地,就像易经中常常提到的,这是一个平衡。
减少将图片写成 Base64
在我们的框架中,当图片大小小于 10KB ,会被转化成 Base64 写到 js 文件中,因此如果对js包有强制要求的情况下,可以选择不将图片写到 js 文件中,通过配置 inlineLimit 实现。
port default { // Default: 10000 (10k) inlineLimit:10 }