大家好
我是创意行业中的前端prupru
是一个虽然面试官说已经没什么人用Angular,
却依然把Angular技能点满的人。
前言
我想推动团队重视网页项目中的优化由来已久,也曾在团队讨论中委婉地提过我们项目的Lighthouse分数不尽人意。虽团队大大们有所认知,但碍于业务的时间紧张和成本限制,往往是我自己抠时间根着教程做一些小修小改。
直到接下来需要我要上手一个一年一度的项目。
TL:你看看去年这个活动网站。
我:嗯……
TL:今年我们还有这个业务。你不是一直想搞优化嘛?看你年轻力壮爱学习,给你几天时间想点办法把Lighthouse分数整上去
我:!!!
(以上对话为虚构)
开心的是,网页优化和最佳实践终于被提上了新项目的日程。
紧张的是,感觉自己的经验不够用。
我的目标是整理出一个可被分享的工作流。
在瞎琢磨了几个小时后,汇报一下初步成果
-Performance指标3项达标,分数从66到70分。
-未使用的文件加载速度 1.04秒 降到 0.57秒。
-覆盖率有所上升(main-es2015.js, style.css)。
webpack-bundle-analyzer 对比图
步骤
分为诊断、优化、再测试,以此循环。
诊断
1.打包
ng build --prod --source-map --stats-json --outputPath=dist
在生产环境中打包。和真正部署不同的是,其中--source-map是为了给source-map-explorer提供信息,--stats-json是为了给webpack-bundle-analyzer提供信息。
在本地serve打包好的文件。
然后在本地查看webpack-bundle-analyzer或者source-map-explorer的打包情况。
(工具的安装和使用见各repo)
2.通过Lighthouse报告,定位prod环境下的问题,从最严重的问题着手开始。
“做正确且困难的事”
案例中:通过上图报告得知,“Remove unused javascript” 没有移除未使用的javascript导致的一系列问题,main.js和style.css的覆盖率较低,导致浪费用户的加载时间和流量。
诊断工具见附录【工具一览】。除了Lighthouse的指导外,各教程文章也有很多。
3. 分析打包情况
这也是我觉得困难的地方,因为好多JS优化的文章说到以上的工具就结束了,结果是我看着分析出来的数据和可视化图仍旧满头疑惑。并没有很多文章说如何给出规律如何找到不对劲的包。
实际上自己上手了之后的确发现每个项目特点不一样,无法以一概全。实施细节也因框架而异。然而还是有共通的思路是可以借鉴的。
整体思路是通过数据找到“不对劲”的包。我们的目标是每一份加载的文件覆盖率高,单个文件的大小不要过大,并且一次不要加载过多,可以在后期的用户互动懒加载。也就是大化小,并且在大小和请求次数中取平衡之道。
粗浅的实践经验之后,在我看来“不对劲”有以下几种
-
1.最明显的是单个文件尺寸过大,或者某一组件/库占比过大。
方法:- 是否该文件书写不当? 从代码上优化。 - 是否引用过多的组件或者库? 拆分。
案例中:如果是单个angular组件的大小十分扎眼,很有可能是它承担了太多职责,非常冗长。则确认一下该组件是否可以进一步拆成小组件。
-
2.如果单个文件中,各个组件大小差不多。
方法:- 是否每个被引用的元素都有使用过(常常忘记)? 删除没有使用的引用变量。 - 是否每个被引用的元素都在被加载的第一时间使用? 拆分非初始渲染需要的部分,再懒加载。
案例中:app提前加载了其他页面,下文讲描述Angular的懒加载。
-
3.不同的文件中重复打包了一个同一个库
方法: 在全局引用这个库
-
4.【Angular】es2015项目却加载了某个CommonJS/AMD模块。
事实上Angular打包时ng build --prod 也会发出警告。
Warning: ... depends on ....
CommonJS or AMD dependencies can cause optimization bailouts.
For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies
方法:- 该库是否有ES模块的替代版本?
使用该版本。
- 或根据以上Angular官方文档,mute关于该库的打包警告。
angular.io/guide/build…
优化
根据项目和问题各取所需。
1)懒加载模块
针对分析篇中的第2种“不对劲”的打包现象:首页文件大,但单个文件中各个组件大小差不多。
先放上官方的懒加载特征模块。总结就是对于首页不需要加载的页面,进行懒加载。
一个Angular初学者的常见疑惑是,为什么要把多个组件打包成一个模块?本质上还是被app根模块引用,有什么区别?我曾经也不以为然,但是遇到app规模变大、路由很多、难以优化时,就有必要注重按模块分类这些组件。
因为在Angular模块中,模块Module具有一个Component不具有的特性,即懒加载。
NgModule默认是加载所有其中组件的,Angular懒加载的具体定义是不在initial bundle中加载,在浏览该模块的路径时才加载。所以也可以把feature module特征模块浅显地理解为页面模块,或者某些条件下出现的功能。
案例:
Before app.routing.module.ts
{ path: ‘pathA’, component: AComponent }
把该组件改造成懒加载的模块,步骤
1.在terminal运行
ng module AModule —module app —route pathA
第一个参数是新建的模块名,app是指新模块要加入的根模块。
2.把原本A.component.ts中Acomponent的内容移动到AModule中的A.component.ts。(注意生成模块时的命名冲突)
After
{
path: 'pathA',
loadChildren: () => import('./famitsu/famitsu.module')
.then(m => m.FamitsuModule)
}
2) 将global library单独打包
针对分析篇中的第1种“不对劲”的打包现象:单个文件中某个库的比例过大,或者第4种:库是CommonJS/AMD模块。
根据webpack-bundle-analyzer的可视图,感觉可以从lottie.js这个库下手。lottie占比看上去很大,同时被打包在了main.js内。
大致思路是webpack中可以划分打包,Angular内置webpack,如何告诉它分开打包呢?
一顿搜索,发现了宝藏Angular官方文章Adding a library to the runtime global scope 和stackoverflow上的Angular Cli Webpack, How to add or bundle external js files?
angular.json中的scripts本来用来添加外部库,其实也可以用来指定本地库的打包。
添加lottie的本地node module路径
angular.json
"architect": {
"build": {
"options": {
...
"styles": [
"src/styles.scss"
],
"scripts": [
"../node_modules/lottie-web/build/player/lottie_svg.js"
]
},
...
}
}
...
打包后,dist文件夹中应该出现一个scripts.js
优点:(推论)如果app发布更新,scripts的部分不变而web app发布新版本,则用户不必重复下载lottie的部分。
优化x1: 发现了min.js文件
"scripts": [
"../node_modules/lottie-web/build/player/lottie_svg.min.js"
]
优化x2: 懒加载库
根据自己对项目的了解,甚至可以将库随着组件懒加载。
观察dist/index.html
<script src="scripts.<hash>.js" defer></script>
思路:配置“scripts”后,打包好的scripts.js被插入首页。那其实只要打包scripts.js但默认不插入首页,然后需要的时候添加一个script DOM就好了。
一顿搜索后,还是上面的Angular官方文章。
1.修改angular.json配置,默认不插入打包的scripts
Before
"scripts": [
"../node_modules/lottie-web/build/player/lottie_svg.min.js"
]
After
"scripts": [
{
"input": "../node_modules/lottie-web/build/player/lottie_svg.min.js",
"inject": false,
"bundleName": "lottie"
}
]
2.在组件中懒加载
这一部偷懒我就没继续了。参考教程 Lazy Loading Scripts and Styles in Angular
3) 针对库自身的优化
和lottie还没完。
在一顿搜索后发现此文Reducing lottie-web bundle size。大意是lottie自带3种渲染器:canvas、html和svg,但是大部分项目在初始化时就指定一种渲染器并且不需要切换。那么只加载该渲染器的部分就够了。
案例中:只使用了lottie的svg渲染器
Before
import lottie from 'lottie-web';
After
import lottie from 'lottie-web/build/player/lottie_svg';
并且,该方法正是lottie的官方angular组件版ngx-lottie。 所以使用ngx-lottie本身也可以提升优化。
优化:CSS篇
打开Coverage,观察未使用的selector的特征。
案例中:发现都是material组件的style。
实际上整个项目只使用到material的6个组件。
我们一般会记得在js/Angular Module里按需取用,却忘了css也是可以如法炮制的。
这也是上篇针对库自身的优化的一种。
1)减少import的内容
Before
@import '~@angular/material/theming';
观察该引用样式的源代码,和node_modules的库路径的其他文件(或者官方仓库)。
在此,观察material button组件的样式代码(左边是~@angular/material/theming,右边是~@angular/material/button/button-theme)
可得theming.scss包含两部分core样式定义,和各组件样式代码。由于我们不需要所组件的样式,并且这些未使用过的组件占比很大,将引用改为如下:
After
@import '~@angular/material/core/core';
@import '~@angular/material/checkbox/checkbox-theme';
...
再次确认样式显示没有不同。
2)针对Angular material,减少生成的组件样式。
Before
@include angular-material-theme($gweb-play-jp-esports-theme);
以下教程完全来自视频 Remove unused Angular Material CSS (2020)
同理观察该mixin的源代码
自定义一个mixin,复制该mixin并删除不需要的组件includes。
After
/* Copied from mixin angular-material-theme */
/* @import '~@angular/material/theming'; */
@mixin custom-theme($theme-or-color-config) {
$dedupe-key: 'angular-material-theme';
@include mat-private-check-duplicate-theme-styles($theme-or-color-config, $dedupe-key) {
@include mat-core-theme($theme-or-color-config);
@include mat-checkbox-theme($theme-or-color-config);
...
}
}
@include custom-theme($gweb-play-jp-esports-theme);
总结
减少文件大小效果立竿见影。 96kb -> 32kb,几乎是三分之一。
附录
工具列表和引用列表
工具一览
source-map-explorer
webpack-bundle-analyzer
Angular虽然内置webpack,但是不包含webpack-bundle-analyzer,所以仍需要添加这个库到devDenpencies。
引用一览
Angular官方文档
Adding a library to the runtime global scope
Configuring CommonJS dependencies
Lazy-loading feature modules
Angular Cli Webpack, How to add or bundle external js files?
其他
Remove unused Angular Material CSS (2020)
Lazy Loading Scripts and Styles in Angular
Reducing lottie-web bundle size
后序
以上一些步骤,是我根据各平台的文章和自己对项目整体的理解摸索出来的。
看到数据上的改善很欣慰,但也烦恼整体覆盖率仍是不算高,未使用的css selector还是占了很多。
在此抛砖引玉,若有点赞或指点不胜感激!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!