vlambda博客
学习文章列表

web性能测试及优化

本文会通过三部分来介绍web性能相关
  • 为什么web性能重要

  • 怎么测试web性能

  • 怎么改善性能

1 Why does speed matter

这部分我不是很想说,就像在搜集证据说钱多么重要一样,但还是应付的给个参考链接:Why does speed matter?。

比如性能关乎用户体验,进一步影响用户留存、转化率。

2 How to measure speed

评测一个web应用包含很多指标,比如RAIL model对response, animation, idle, and load四个方面进行了评估。

另外推荐一些常用的测试工具,比如Chrome DevTools,WebPageTest,lighthouse。

其中WebPageTest是一个在线的测试网站,可以对指定域名生成测评报告,结果如下

web性能测试及优化

lighthouse可用chrome自带版本,也可以使用lighthouse-cli,结果如下

web性能测试及优化

带有--locale=zh-cn参数可以指定中文报告,打分指标参考这里。
PageSpeed Insights也使用了lighthouse的数据。

3 How to improve performance

这部分我打算分两个部分介绍

  • 内容加载

  • 内容渲染

3.1 内容加载

这部分介绍页面怎么减少内容加载对网站性能的影响,可以分为以下思路,即一个页面的加载,从请求、传输、下载以及其他细节方面来考虑

3.1.1 减少请求次数

  • 利用ssr,加快首屏渲染,有利于seo

  • http2的server push,也能加快首屏渲染,但和seo没关

  • 用css样式代替图片或雪碧图或者base64表示图片

  • 代码打包合并

  • 本地存储

3.1.2 减少下载次数

  • 使用http缓存

3.1.3 减少下载总量

  • 压缩资源,包括代码和图片等,包括打包时的压缩和http压缩

  • 少使用第三方library

  • tree shaking

  • 将部分第三方包由runtime提供,比如webpack的externals,多用于library开发

3.1.4 减少网络传播时间

  • cdn

  • http/2

  • 减少header payload

3.1.5 其他

  • 代码拆分

  • 懒加载,比如script的defer或async属性或者按需加载

  • 预处理,link标签的rel属性相关的优化,包括

    • dns-prefetch 提前解析dns

    • preconnect 提前建立连接(包括dns查询、tcp连接和tls协商)

    • prefetch 下次导航时可能需要,应该提前获取

    • prerender 下个导航可能需要,应该提前执行,比如渲染一个html

    • preload 相对于prefetch优先级高,且是当前导航中需要的资源

3.2 内容渲染

本部分会结合页面的渲染中的五个步骤,对其进行优化,还会参考Rendering Performance。
大部分设备屏幕刷新率为60fps,这样渲染时的每一帧需要16ms的准备时间,每一帧期间除渲染本身以外的其他工作只有10ms,如果超过这个时间就会掉帧,这被称作 jank。

3.2.1 parse

渲染的第一步本是解析html,这里代指所有会引起页面渲染的操作,比如动画、过渡、scroll或其他交互。

  • 首次渲染,首次渲染时诸多因素与内容加载有关系,注意遇到script会阻塞页面解析,css随不会阻塞,但是页面要等cssom解析完成才能下一步操作,具体参考关键路径优化。

  • 使用window.requestAnimationFrame()可以在帧开始执行画面修改,而不是其他timer,因为后者可能发生在帧的结尾时刻,导致丢帧。

  • 对于长时间的工作,如果是纯计算等可以使用web worker完成的就开一个worker线程,否则对于优先级高的任务可以使用window.requestAnimationFrame(),优先级低的使用window.requestIdleCallback()将复杂计算转换成多个小计算来进行。

  • 少操作dom,dom操作本身就很消耗性能

3.2.2 Style calculation

样式计算分为两个步骤

  • 计算出哪些元素应用当前样式,这一步可以通过降低选择器复杂度来解决,比如使用BEM命名规范,具体选择器解析顺序是从右到左,创建选择器时应加以注意。

  • 将样式匹配到对应的元素上,这一步可以通过减少要计算样式的元素数量解决。

3.2.3 layout

layout是计算各需要显示元素的坐标

  • 选用不影响元素坐标的动画或过渡,包括 transforms or opacity

  • 使用flexBox而不是旧的模型,后者盒子多

  • 避免forced synchronous layout造成的卡顿或layout thrashing。一个页面包含两个tree,其中一个表示实际的dom,一个表示内存中被js操作的dom,操作前者很耗费性能,当读取元素样式时会强制元素渲染,即修改实际dom,在执行一系列dom操作时应避免这种情况,比如

function resizeAllParagraphsToMatchBlockWidth() {

// Puts the browser into a read-write-read-write cycle.
for (var i = 0; i < paragraphs.length; i++) {
paragraphs[i].style.width = box.offsetWidth + 'px';
}
}
复制代码

应修改为

// Read.
var width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth() {
for (var i = 0; i < paragraphs.length; i++) {
// Now write.
paragraphs[i].style.width = width + 'px';
}
}
复制代码
  • 使用DocumentFragment组织连续dom的插入,参考Document Fragments and why you should use them,如果单独插入,每次插入都是一个宏任务,都会引起页面渲染,如果用该方法可以减少渲染次数。

3.2.4 paint

这里计算绘制顺序,layout后就会paint,这一步没有特别的优化步骤

3.2.5 compositing

这一步将前面获取的信息分层并分别栅格化

  • 利用willchange或transform: translateZ(0)将需要改动的元素分到单独的层,主要分层本身就消耗性能,不要对过多元素使用

  • 降低动画复杂度,那些涉及模糊的消耗性能更多,比如background: red; 和 box-shadow: 0, 4px, 4px, rgba(0,0,0,0.5); 相比

  • 对于标记为Non-Fast Scrollable Region的区域,即有事件绑定的区域,如果存在交互,会触发main线程处理,可以在事件监听器使用passive: true解决,具体参考这里。

  • throttle和debounce