[Vue] 跟着 Vue 闯荡前端世界

针对第三方套件使用的合宜性来进行审视,并透过 async component 与 webpack 代码分割功能来实现 Lazy Loading Routes 机制,让打包后产出的 boundle 文件能依照各页面需求载入所需的代码。


前言


在网站开发过程中,通常被关注的点只会在功能正确性上,但随着日子逼近上线期,渐渐地就会开始关注透过 webpack 打包出来的那包文件,并且在意“进入网站”所需载入的文件大小及时间,此时文件大小就会成为锱铢必较的议题。笔者整理了一些近期项目的瘦身心得,以下说明。

vue-cli: v2.9.2
vue: v2.5.2
vue-router: 3.0.1
webpack: 3.6.0

检查 boundle 文件内含物


在默认情况下,绝大部分的程序透过 webpack 打包后的会存在以下两只 boundle 档中:

  • vendor.[hash].js : 第三方套件
  • app.[hash].js : 大部分开发的代码都于此

要解决文件过大的问题,首先必须知道这些 boundle 档究竟包含了那些代码模块,这样我们才可以对此进行分析及拆解处理;若是使用 vue-cli 建立的项目,可直接加入 --report 参数来产出这个分析文件。

在打包的过程中主要是利用 webpack-bundle-analyzer 这个插件来分析各个 boundle 文件;因此若不是透过 vue-cli 产出项目架构的朋友,也可以直接加入此套件进行分析即可。

建置后就可以看到所有 boundle 文件各自的内含物,区块越大表示文件越大,因此可由此审视过大的组件是否有其存在的必要;另外,也可以做为瘦身计划的成果表,由此显示图形化的成果是否有如预期变化 。

清查使用套件


由于第三方套件在打包后都会放置在 vendor.[hash].js 中,所以稍有套件滥用的情况就会造成该文件异常肿大,因此这只 boundle 档会是我们关注的重点项目之一;透过 boundle analyzer 产出的区块图可以很清楚看出文件相对大的套件项目(因为字特大特清楚),这时可以考虑的方向如下,后续会针对各情境进行说明。

  1. 是否真有使用到这个套件
  2. 套件是否包含无用的文件可移除 (ex. 各国语系档)
  3. 套件是否只在少数页面使用,可作动态载入 (有需要在用)
  4. 套件是否能够仅载入需要的部分就好,不要整个都载入

似乎没使用到这个套件

在开发期间,会因为特殊需求会载入相关的套件来加速开发,但大家都知道需求的变动是很快的,有时候会有需求已被移除但忘记移除套件的情况发生,就会造成无用套件仍然被包进程序中,因此藉这个机会清查一下套件清单也不错。

套件存在无用的语系文件 - moment.js

从分析图可以清楚发现在 vendor.[hash].js 中 moment 套件中被放入各国的语系文字,光是 moment 套件就占了 221.08 KB 大小,而事实上我们只需要加入系统需支持的语系即可,因此若可以移除其他不须的语系档应可大幅减少 boundle 文件的大小。

我们可在 webpack 使用 ContextReplacementPlugin 来设定 moment 只需要 en 及 zh-tw 语系即可。

调整后的文件已经从 221.08 KB 减为 50.52 KB ,足足瘦身约 171 KB 左右,是不是相当有感!

只有特定页面才需要的套件 - highcharts.js

先前有提到第三方套件都会放置在 vendor.[hash].js 中,也就是表示进入网站就会载入这只 boundle 文件,但若特定套件如 highcharts 只在少数页面才会用到,而其他页面也需花时间载入这个没用到的套件,这样不是很不符经济效益吗?

这时我们应将 highchars 从 boundle 档移出,透过 webpack 设定将这套件切出 highcharts.[hash].js 独立的 chunk 文件,让有需要的页面才去下载,这样才是比较合理的作法。

这样又让 vendor.[hash].js 瘦身 221.48 KB,让进入网站的客户少花一点时间载入不必要的文件。

只单独载入所需模块 - lodash.js

在使用一些 Functional Library 的时候可以特别注意一下 import 方式,是否可以只 import 所需 Function 就好,避免将整个 Library 都载入而造成 vendor.[hash].js 肿大。以 lodash 为例,大家可以考虑 one-by-one 的方式来只 import 所需 function 会比较好喔!

// X: Import the whole library
import _ from 'lodash'

// O: Import specific methods one by one
import map from 'lodash/map'

路由页面组件分组


当网站开发越多功能时,可以发现 app.[hash].js 逐渐加大,因为默认会将网站中非套件类的代码都打包至 app.[hash].js 这个 bundle 文件,这也就表示只要访问站台,就必须要把整站所有代码都载下来,这样会花费许多不必要的等待时间;因此我们可以透过 vue 的 async component 搭配 webpack 设定来拆出各页面所需的独立 boundle 文件,只有在访问到该页面时才需载入,实现 Lazy Loading Routes 机制。

以下是一个开发中的项目,在打包后产出 app.[hash].js 约为 2MB,这代表用户在初次访问时就必须花费时间等待下载 2MB 文件只为了显示一个画面;而且这只是开发中的项目,如果全部功能都完成后,也许会来到 4MB 的大小,所以必须拆解 app.[hash].js 中的代码,让访问页面时才载入该页面所需的代码。

在 router 中可用 dynamic import 载入各页面组件,并可依据功能名称来 group 相关页面组件在相同 chunk 中,如果打包出来的 chunk 档仍然过大,也可再考虑细拆让特定页面组件自己存在一个 chunk 也行。

当 chunk 切得过细,就又要考虑到是否有重复程序不断出现在各 chunk 中,一直载一样的东西浪费网络流量;此时可考虑设定 webpack.optimize.CommonsChunkPluginminChuncks 来定义有多少重复份数代码散在各 chunk 时需要再抽出成为一个共用 vendor-async.[hash].js 文件。不过这是双面刃,如果将该值设太大,表示只有少数组件共用次数超过,所以还是会有重复下载的流量浪费;如果设太小,等于共用两次的东西就包到共用区,有可能刚进网站根本没用到该功能就要载入了。

接着调整 Webpack 设定,让 boundle 文件名称具有意义。

完成上述调整后 app.[hash].js 已从 2MB 降到 592 KB,减少约 1.4 MB 无谓的数据下载时间;另外当访问到特定功能如 mobile 相关功能页面时,也只需要再加载 mobile.[hash].js 就可以,这样可以避免不必要的资源一次全部载入。

参考资讯


Lazy Loading Routes

Dynamic & Async Components

Optimize your libraries with webpack


希望此篇文章可以帮助到需要的人

若内容有误或有其他建议请不吝留言给笔者喔 !