前段时间寻思着开启 Angular 的热更新,同时公司项目打包耗时太长,于是就研究了 webpack 打包优化,不仅过程不是很顺利,而且结果也不是很美好,但还是小小的记录下吧。
开启热更新
Angular 默认未开启模块热更新,需要手动开启,大致有如下步骤
- script 脚本添加 --hmr 参数
- 安装 @angularclass/hmr 包
- 环境配置添加 hmr 参数,后续代码中需要用到
- 添加 hmr.ts 文件,具体内容直接 copy 官网即可
- main.ts 中添加 hmr 相关代码
- tsconfig.app.json 添加
"types": ["node"]
,避免编译报错 - angular.json 下 server.options 添加
"hmrWarning": false
,去掉启动 warning
打包原则和常见方式
常见优化原则
- 缓存(cache)
- 并行化(多线程)
- 较少任务量(限定范围)
常见优化手段
- 分开 vendor 和 app:DllPlugin & DllReferencePlugin
- uglifyJsPlugin 非常耗时,开启并行配置
- parallel
- cache
- HappyPack
- 串行转并行
- babel-loader 非常耗时
- cacheDirectory 开启缓存
- include & exclude
NG 内置命令打包
已有的打包情况
- ng build:没有 hash 值,有 source map
- vendor.js: @angular、three、rxjs、svg.js、svg.draw.js
- polyfills.js es2015-polyfill.js runtime.js
- style.js:处理样式
- main.js 业务代码
- ng build --aot:提前编译,没有 hash 值,有 source map
- 代码相对不使用预先编译,体积要小一点,因为可以去除 JIT 编译相关的代码
- 具体代码结构和不适用 aot 是差不多的
- ng build --prod:有 hash 值,有 source map,默认开启 aot
- 相比上两者时间构建时间变长了,主要是因为进行了代码压缩,体积小了很多,文件输出稍微有点变化
- polyfills.js es2015-polyfill.js runtime.js 三个还是有
- 少了 style.js,多了 styles.css
- 少了 vendor.js,main.js 包含了之前的 vendor + main
- ng build --prod --vendor-chunk
- 熟悉的感觉 vendor.js 又回来了,出于业务代码与依赖分离的角度,同时高效利用客户端缓存,--vendor-chunk 参数的添加还是很有必要的
- 不重复编译 vendor 是我考虑优化的主要方向
Angular-处理自带参数功能
- --build-optimizer: 更小的 bundle,但更长的构建时间
- --vendoor-chunk:将所有第三方库代码提取到单独的块中,也有利于持久化缓存
NG 限制
NG 7.x 之后并不支持弹出 webpack 配置,经查阅,如下方式可选
- 放弃 CLI,自己书写构建脚本
- 使用
ngx-build-plus
扩展 webpack 配置
放弃 CLI 似乎有点大胆,于是尝试使用 ngx-build-plus
方式,优化思路
- ngx-build-plus 模块,使用 webpack-merge 合并配置
- 开启:happypack、parallel、DllPlugin、DllReferencePlugin
修改 angular.json
"builder": "@angular-devkit/build-angular:browser", =>
"builder": "ngx-build-plus:build"
修改 package.json 指定额外的配置:--extra-webpack-config ./webpack.config.js
关于 angular/cli 是否支持 Dll?作者和成员回答是不支持。但有人写了个 loader,但是 star 并不高,但作者有在维护,提 issue 反馈很快。
如果将 angular 模块打包成 dll,会导致路由模块懒加载不打包,因此需要 webpack-dll-ng-module-loader
DllPlugin 和 DllReferencePlugin 原理
- DllPlugin 预先生成 dll.js 和对应的 json 文件
- 在每次编译过程中,DllReferencePlugin 使用 json 文件来判断用户请求的文件是否是一个 dllModule,如果是的,webpack 就不会打包进来
- Dll 这个概念借鉴了 Windows 系统的 dll。一个 dll 包,就是一个纯纯的依赖库,它本身不能运行,是用来给你的 app 引用的。
dll 版本号,自动清理过期文件,自动加入 index.html
结果
不知道是项目体量的原因,还是 Angular 自身的原因,热更新表现不尽人意,更新很慢,还容易内存溢出,直接爆掉。
打包优化,最后实践是成功了,但我并没有运用到实际项目中了,因为测试结果,打包速度的并没有提升很大,表示很费解,明明就少打包了那么多文件。
后续
今日(20190926)尝试了一下 HardSourceWebpackPlugin
插件,也是利用缓存的原理,但结果令人失望,你没有听过,结果慢了 23 秒,真扎心。
关于 dllPlugin 的使用,应该会有这样一种感觉,就是配置太繁琐了,社区有一个 AutoDllPlugin
可以解放你的配置负担。你需要知道的是 vue-cli 和 create-react-app 已经抛弃了 dll 的使用,因为 webpack 4 有着比 dll 更好的打包性能。
思考
至于为什么如今使用 dll 等一下插件,优化效果并不明显呢?我想可能是 webpack 发展到现在,内置了很多的优化,比如缓存等,从而导致效果不是很明显。
之前自己的关于缓存的思考局限了,简单了认为我项目下并没有新增文件,怎么可能开启的缓存呢。殊不知缓存可能会在 node_modules
文件夹下,比如 .cache
文件夹。同时 Node 具有文件访问权限,可以写入到你磁盘中几乎任意地方,比如你用户目录下。