• 中文
  • ENGLISH
前端代码打包优化
2018/03/08

本文作者:天尹

前端资源打包在每个项目中都会有涉及,每位开发者都希望打包是用最少的时间构建出最小的代码,这不仅能提高团队中的效率,也能提高页面的访问性能,以下会从如何优化构建速度和优化构建输出代码来说明一些方法。
图片来自webpack官网log

1. 速度优化

日常开发打包配置大家都是习惯用脚手架等的默认配置,没问题,没毛病,跑的好好的,就没这么在意。对于一些强迫症的患者,还是会有点不爽的, 比如速度,比如最终打包出来的资源大小等等。

1.1 本地构建或者服务端构建

1.1.1 本地构建

开发完后本地构建,然后通过push到cnd同步资源。可能是传统大家喜欢做的思路,没毛病,也挺好用的。
不足点:加重了仓库的体积,对于仓库中的语义化的npm包,本地构建不能实时享受到包的更新。
优点: 适合于多人项目合作初期,或者依赖的一个三方包也处于一个不断迭代的过程,每个人开发的过程中仅仅打包自己的页面,互相不干扰

1.1.2 服务端构建

服务端构建,大致就是当资源push的时候,会在一台构建机器上面跑类似ci的服务,同时也会有打包的服务
优缺点基本就是本地的相反,但是还是比较推荐这样的方案

1.2 如何来优化

1.2.1 配置差异化

粗暴点其实大家可能希望这个配置可以自动化生成,而且可以仅有一份来做,思路是没错,但是其实应该做一些区分
功能
本地开发
线上发布
压缩代码
需要
babel-polyfill
一般不需要
看业务需求
分离样式
需要
删除console.log
需要
css Prefix
需要
OccurrenceOrderPlugin
需要
DedupePlugin
需要
Babel present转码
需要
其实比较合理的方式应该是用环境变量来区分进行不同环境下不同的配置,ps:设置环境变量为了在window兼容,可以使用cross-env 来设置
以上的对比没有进行测试,感兴趣的同学可以试试看,在老的基础上修改会有多少的优化。
总结起来就是本地开发只求速度快,能少处理一点是一点

1.2.2 常见方式

可以在社区中看到很多相关类似的文章来做webpack的优化,各种各样的提速,可能都知道,但是懒得做,但其实一旦完成后,带来的受益是巨大的。
externals
这可能是最暴力最提速的方法之一,把其中的一些库从中忽略掉,如果extern react了,需要注意的是最好把相关的一些react-addons-transition-group也给extern掉,否则有可能会出现依然打入多份react的问题,因为react-addons-transition-group这样的包里面代码是类似如下方式,externals并不能排除
module.exports = require('react/lib/ReactTransitionGroup');
Dll
将一些可预见性的库从中抽离,预打包,可以极大的提速,当时还是有蛮多需要注意的,比如同样的包最好全局只有一份,预打包后不能享受到语义化版本的资源跟新,需要结合实际问题来看是否需要。
HappyPack
常用套路加速
const os = require('os');

HappyPack.ThreadPool({size: os.cpus().length })
一些配置
设置一些alias,同时可以适当设置一些loaders中的exclude等
设置css-loader版本号
提速特别明显
"css-loader": "^0.14.5",
相关Issue
替换scss-loader 为fast-sass-loader
相比起来比scss-loader速度更快
不用webpack自带的uglfiyJS
用自带的uglfiyJS来做压缩速度比较慢,这边有俩思路,但原理应该是一样的
  1. 造个新轮子多核并行去压缩js和css
这个方案优化一般来说可以提速一半左右
js和scss的分离
这个可以优化本地开发过程中的rebuild速度,尽量让scss文件和js文件分离,如果使用了一些ui库,可以引用UI库的css文件,而不是scss文件,省去每次的scss build过程

1.3 其他

  1. 对于打包webpack可能是一个功能大而全的工具,除此之外还有很多类似于rollup或者是browserify,要看具体场景来使用,杀鸡可能选个更合适的刀会更好,不要盲目选择都是用一把刀。后续待尝试后详细再补相关的一些其他打包方案。
  2. 优化永无止境

2. 代码优化

2.1 精简node_modules

现在开发基本都是使用npm或者yarns进行依赖管理,随便引入几个依赖就会使得最终打包的结构臃肿,再加上开发者可能对依赖的包并没有特别统一管理,需要什么就引入什么,不会去关心互相之间的关系。
上图仅仅是说明node_modules管理的时候出现的包的量一个图,可以说是非常的形象。图片来自文章What’s really wrong with node_modules and why this is your fault,文章推荐一看
注:npm包开发者应该保证包里面仅只有需要的代码,比如测试等等的资源最好都忽略掉,这样也可以省去不少开支,细节后续会整理一篇新的文章。

2.1.1 方法1:同样功能使用同样的包

多人开发常会遇到使用三方库,部分人使用deep-extend,但是后面可能又有人用lodash,这样一来一回,会出现同样功能的包会引用多个的问题。对于这个情况不会导致bug,但是会造成node_modules增多且package.json依赖混乱,当然代码大小也会有相应的损失。
解决方式
查看仓库的package.json,比如
{
    "deep-extend": "^0.4.1",
    "lodash.clonedeep": "^4.5.0",
}
如上俩个库都是想做深拷贝,可以选择只使用其一即可,推荐

2.1.2 方法2:同一个包尽量不存在多个版本

大部分情况这样仅只会加重代码的体积等,但是在少数的场景下,比如使用了前端组件库;
举个例子,项目中依赖了俩组件A和B,A依赖了某UI库C的1.0.0的input,而B依赖了C中的2.0.0的input,最终页面上会同时存在俩版本的input,这里存在一个隐患(如果俩组件有Dom节点结构调整发生样式变化,这个时候无论是使用1.0.0或者是2.0.0的样式其实都不合适),所以这个问题也需要多关注的,特别是前端的UI库,最好习惯性的排查下较为好。必须最好做下
解决方式
打开chrome调试工具,查看node_modules,对于UI的库,仔细翻翻,不能有多版本的库,对于其他库则可以佛系排查;如果发现某个库A出现了多次,可以使用npm ls A来查看是在什么地方多次引用到了,然后再定位到具体细节的包查看

2.1.3 方法3:分析代码依赖

使用webpack的使用BundleAnalyzerPlugin或者是用自带的功能输出json,进行分析,排查为什么最后输出的资源这么大的原因

2.2 抽离公用资源

比较适合那些整站的开发,将各个页面公用的资源抽离出来,这样在页面的访问过程中浏览器可以很好的将这些通用资源缓存,类似使用dll存在本地工程中,还可以使得打包加速,下面以dll为例
做法
  1. 配置好项目所要打的dll资源,一般选择的是一些三方的库,具体看项目的需求
  2. 预先打好dll的资源放到项目的某个自定目录中(甚至可以直接打成生产环境的版本省去后续的压缩)
  3. 本地构建或者服务端构建任务结束后,将打好的dll资源拷贝到build目录下面
注意点
  1. 如果使用服务端构建请务必保持本地的npm版本和服务端构建上面的版本一致,不同的npm的版本可能会导致manifest.json的里面内容不一致,因为dll存的是路径
  2. 最好解决下同一个库多版本的问题,否则dll中就会打进去多版本的库,因为dll存路径不同的版本的版本号是不一致的

2.3 其他

2.3.1 使用babel-preset-env

官方也是推荐使用它来代替babel-preset-2015,根据业务所需要适配的浏览器去选择合适的,否则使用多余的转码会是的代码转码后更大,同时对于browsers的设置,也可以跟postCss进行统一

2.3.2 区分好大包小包的问题

一些组件库或者是lodash
import { isEmpty } from 'lodash';

import isEmpty from 'lodash/isEmpty';
所产生的结果是不一样的。除非使用一个转码的工具来支持;vscode用户建议装一个Import Cost

2.3.3 升级打包工具webpack

升级到webpack3,有tree shaking、Scope Hoisting都对代码有着不错的优化

2.3.4 优化样式

  1. prefix这类的工作交给postCss来完成,同样是根据业务的需求去做相关的prefix的处理,不多不少
  2. 精简样式,去除没必要的样式

订阅我们