什么是web前端性能
Web performance refers to the speed in which web pages are downloaded and displayed on the user’s web browser(维基百科)。
简单点说,就是网页在浏览器中下载,呈现以及交互的流畅程度。
从输入URL到页面加载发生了什么
要弄清楚什么是加载性能及优化手段,就得知道从输入URL到看到内容整个过程发生了什么。
哪些因素影响资源加载速度
1、带宽
2、资源大小
3、http响应速度
4、缓存
浏览器渲染原理
资源加载完成后浏览器得完成资源解析和渲染,渲染的速度直接影响用户体验。不同的浏览器内核渲染流程会不一样,大致如下:解析DOM-解析CSS-生成渲染树-绘制和呈现。
前端性能指标
搞清楚了浏览器从加载到渲染两个核心流程,我们看看到底有哪些量化指标来评价一个web应用的性能。
- FCP(First Contentful Paint)白屏时间,值越低越好;
- SI(Speed Index)页面渲染时间,值越低越好;
- LCP(Largest Contentful Paint)可视窗口最大内容渲染时间,值越低越好
- TTI(Time to Interactive)用户可交互时间,值越低越好;
- TBT(Total Blocking Time)用户行为阻塞时间,值越低越好;
- CLS(Cumulative Layout Shift)可视窗口中累计可见元素布局偏移;
- FID(First Input Delay)用户首次交互时间,值越低越好
FCP白屏时间
白屏时间指的是用户在浏览器中打开页面到渲染第一个DOM元素所花费的时间,DOM元素包括图片,非空白canvas,SVG等元素,不包括iframe中的元素;
FCP优化策略
1、DNS解析优化
DNS缓存优化
DNS预加载策略
稳定可靠的DNS服务器
2、服务端处理优化
Redis缓存、数据库存储优化或是系统内的各种中间件以及Gzip压缩等…
3、CDN加速
4、精简DOM结构,合理压缩和放置CSS,JS
5、字体加载优化
@font-face {
font-family: 'Pacifico';
font-style: normal;
font-weight: 400;
src: local('Pacifico Regular'), local('Pacifico-Regular'), url(https://fonts.gstatic.com/s/pacifico/v12/FwZY7-Qmy14u9lezJ-6H6MmBp0u-.woff2) format('woff2');
font-display: swap;//注意这里
}
复制代码
浏览器经常会出现一些出乎我们意料的问题,而字体的加载就是其中之一。大多数浏览器在自定义字体还未下载之前会先隐藏文本。一般情况下,我们可能没有感知,但是网速较慢的情况下可以看到,大部分浏览器会隐藏文本一定时间直到字体加载完成,如果字体没有加载完成,甚至不会显示文本。
这个时候font-display属性就起到作用了,具体如上代码。它不仅提供了自定义字体和内容的可访问性之间的最佳平衡,还提供了和使用JavaScript脚本相同的字体加载行为。当然,这个属性存在一定的兼容性,但是问题不大。
如果字体肯定会用到,我们甚至可以预加载字体。
<link rel="preload" as="font">
复制代码
SI(Speed Index)页面内容呈现的速度
SI优化策略
1、优化主进程(renderer process),包括解析HTML,创建DOM树,解析CSS,执行JavaScript
- 优化第三方JavaScript(First Load Time)
- 利用防抖优化用户输入事件
- 利用web worker
- 减少复杂的样式计算和嵌套
- 避免大的、复杂的布局和布局回流;
- 简化页面绘制和减少绘制区域
- 减少冗余代码
- 8、异步加载非必须的css
<noscript><link rel="stylesheet" href="https://juejin.cn/post/styles.css"></noscript>
复制代码
LCP(最大内容渲染速度)
LCP measures when the largest content element in the viewport is rendered to the screen
哪些元素可能是最大的?
图片
- 里面的图片
- 带有通过url加载背景图片的元素,不包括css gradient
- 包含文本节点的块级元素或者包含文本节点的子元素;
注意:只有当元素在可视窗口才可能被认为的最大渲染内容,最大元素是可变的。
如何监测最大元素
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
复制代码
如下图,页面渲染及浏览的不同阶段,可视窗口最大元素是发生变化的。
TTI(用户可交互速度)
- 页面呈现的可用内容速度,比如最大内容渲染速度;
- 大多数页面元素注册的事件,并且用户交互响应速度在50毫秒以内;
TTI优化策略
1、加载采用PRPL模式
- Push (or preload) the most important resources.
- Render the initial route as soon as possible.
- Pre-cache remaining assets.
- Lazy load other routes and non-critical assets.
2、减少因代码切割导致的依赖
import moduleA from "library";
form.addEventListener("submit", e => {
e.preventDefault();
someFunction();
});
const someFunction = () => {
// uses moduleA
}
复制代码
form.addEventListener("submit", e => {
e.preventDefault();
import('library.moduleA')
.then(module => module.default) // using the default export
.then(someFunction())
.catch(handleError());
});
const someFunction = () => {
// uses moduleA
}
复制代码
上述两段代码功能目的是一样的,都是监听事件执行相关代码,第一段代码是先引入依赖模块,而第二段代码是在事件触发时候按需引入模块。这里可能有人会问,岂不是每次监听到事件都会引入一次?实则不然,ES6中,如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
3、去除无用代码
可以通过eslint,webpack等工具发现和去除无用的冗余代码。
4、压缩代码和数据
无论是早期的grunt,gulp,还是现在流行的webpack,都提供了快捷方便的代码压缩工具。
TBT(响应延时)
页面响应到用户可交互之间阻塞的总时长,比如鼠标点击,屏幕触摸,键盘输入等,即从FCP到TTI的总时长,超过50ms就被认定为一个长时任务。
其优化策略总体上跟TTI类似。
CLS(累计布局偏移)
页面整个生命周期所有元素发生的非预期的布局累计偏移值。
出于交互原因,页面上通常有很多未加载或者隐藏的元素在某些事件触发后展示,这就算导致周围布局发生变化,比如常见点击展开,图片加载等等,甚至修改一些元素的属性时候,比如宽高,会导致页面重排,也会消耗渲染性能。
如何减少CLS
- 给image,video或者其他具有长宽比的元素设置长宽值;
- 尽量不要在已经渲染的内容前面插入内容;
- 尽量选择transform animations属性去触发布局变动;
<img src="https://juejin.cn/post/puppy.jpg" width="640" height="360" alt="Puppy with balloons" />
复制代码
img {
aspect-ratio: attr(width) / attr(height);//宽高比
}
复制代码
<img
width="1000"
height="1000"
src="https://juejin.cn/post/puppy-1000.jpg"
srcset="puppy-1000.jpg 1000w, puppy-2000.jpg 2000w, puppy-3000.jpg 3000w"
alt="Puppy with balloons"
/>
复制代码
上面提到很多性能可量化标准,那具体该如何测试获取标准数据呢?
network
基本上现代浏览器都提供了强大的network工具,方便查看所有资源的加载信息。也是前端开发最常用的工具之一。
Perormance
当发现web应用有卡顿现象时就得查看Perormance,它能快速定位页面渲染过程中各个环节的耗时,如果发现某个Activity耗时明显异常,就得逐步去排查对应位置的代码了。
Lighthouse
Lighthouse从专业角度给出具体web应用的评分及其优化手段,可以根据其提供的优化策略按需修改。
PageSpeed Insights
这个工具是一个网站,跟Lighthouse比较类似。
chrome://inspect/#devices
相信很多前端开发在开发移动端web应用时或多或少会遇到一些PC浏览器模拟器正常但是手机上就是有问题的情况,毕竟手机性能,webview等太多环节会影响用户体验,这个时候最有效的办法就是利用浏览器自带的远程调试工具,比如chrome的inspect,能够真实的还原问题场景,并分析存在的问题,逐个解决。
Yahoo35条军规
最后还是介绍下Yahoo35条军规,早期是前端开发人员奉行的经典,现在依然有效。
- 尽量减少HTTP请求数
合并文件、CSS Sprites、行内图片 - 减少DNS查找
- 避免重定向
- 让Ajax可缓存
- 延迟加载组件
- 预加载组件(无条件预加载、条件性预加载、提前预加载)
- 减少DOM元素的数量(700以内HTML标签)
- 跨域分离组件(静态分离,同域名并行下载一般不能超过6条)
- 尽量少用iframe
- 杜绝404
- 把样式表放在顶部;
- 避免使用CSS表达式;
- 选择舍弃@import;
- 避免使用滤镜;
- 去除重复脚本;
- 尽量减少DOM访问(缓存已访问过的元素的索引先“离线”更新节点,再把它们添到DOM树上,避免用JavaScript修复布局问题);
- 用智能的事件处理器(比如DOMContentLoaded 代替load,合理使用事件委托);
- 把脚本放在底部,合理使用defer,async异步加载;
- 把JavaScript和CSS放到外面;
- 压缩JavaScript和CSS
- 优化图片;
- 优化CSS Sprite;
- 不要用HTML缩放图片;
- 用小的可缓存的favicon.ico(越小越好,一定要有,否则会造成404);
- 给Cookie减肥(清除不必要的cookie、保持合理大小,合理域名、有效期);
- 把组件放在不含cookie的域下;
- 保证所有组件都小于25K;
- 把组件打包到一个复合文档里;
- Gzip组件;
- 避免图片src属性为空;
- 配置ETags(服务器)
- 对Ajax用GET请求(服务器);
- 尽早清空缓冲区(服务器);
- 使用CDN(Content Delivery Network);
- 添上Expires或者Cache-Control HTTP头
小结
前端的性能优化是个综合性问题,涉及到前端开发的方方面面,以上是对web性能分析和优化的基本整理,至于具体的框架,应用类型不同,优化的手段也会不同,没有一种优化手段是万能的,比如vue,react,angular,小程序等等,都有各自的原理和性能优化手段,具体问题还需具体分析。