前端性能杂谈

Arthas Zheng

  • Pinterest 的搜索引擎流量和注册人数增长 15%,得益于其感知等待时间减少 40%
  • COOK 的转化率提升 7%跳出率下降 7%,且每次会话浏览页数增加 10%,得益于其页面平均加载时间减少 850 毫秒。
  • BBC 发现其网站的加载时间每增加一秒,便会多失去 10%用户
  • DoubleClick by Google 发现,如果页面加载时间超过 3 秒,53% 的移动网站访问活动将遭到抛弃。

如何评估性能

RAIL标准

在 100 毫秒以内响应

在 10 毫秒内生成一帧

最大程度增加空闲时间

在 1000 毫秒以内呈现内容

Response - 在 100ms 内响应

延迟 用户反映
0~16ms 流畅
0~100ms 感觉操作立即反馈
100~300ms 轻微延迟
300~1000ms 感觉到正在加载
1000+ms 转移注意力
10000ms 失望,放弃

Animation - 在 10ms 内生成一帧

预算:16ms

执行时间:10ms

Idle - 最大程度增加空闲时间

减少 long task,任务分块,保证单个任务小于执行时间 50ms。

Load - 1000ms 内呈现内容

建立以用户为中心的性能指标

  1. 性能数据需来源于真实用户
  2. 性能评估要基于真实用户体验

性能数据需来源于真实用户

性能评估需基于真实用户体验

如何准确地衡量用户真实感受到的性能?

性能监控

指标
FP(First Paint)
FCP(First Contentful Paint)
FMP(First Meaningful Paint)
TTI(Time to Interactive)
First CPU Idle
DOMContentLoaded
onLoad
Longtask 跟踪
输入延迟跟踪
... ...
  • 本地性能指标
  • 用户性能指标

本地性能指标

  • 页面总大小
  • 请求总数
  • 是否开启压缩
  • 图片格式
  • 是否懒加载
  • 缓存
  • ... ...

用户性能指标

performance.timings

const t = window.performance.timing;
const times = {};
// 【重要】页面加载完成的时间
// 【原因】这几乎代表了用户等待页面可用的时间
times.loadPage = t.loadEventEnd - t.navigationStart;

// 【重要】解析 DOM 树结构的时间
// 【原因】反省下你的 DOM 树嵌套是不是太多了!
times.domReady = t.domComplete - t.responseEnd;

// 【重要】重定向的时间
// 【原因】拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
times.redirect = t.redirectEnd - t.redirectStart;

// 【重要】DNS 查询时间
// 【原因】DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
// 可使用 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)
times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;

// 【重要】读取页面第一个字节的时间
// 【原因】这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么?
// TTFB 即 Time To First Byte 的意思
// 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
times.ttfb = t.responseStart - t.requestStart;

// 【重要】内容加载完成的时间
// 【原因】页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
times.request = t.responseEnd - t.requestStart;

// 【重要】执行 onload 回调函数的时间
// 【原因】是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
times.loadEvent = t.loadEventEnd - t.loadEventStart;

// 【重要】白屏时间
times.blankTime = (t.domInteractive || t.domLoading) - t.fetchStart;

// DNS 缓存时间
times.appcache = t.domainLookupStart - t.fetchStart;

// 浏览器卸载前一个页面(同一个域下)的时间
times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;

// TCP 建立连接完成握手的时间
times.connect = t.connectEnd - t.connectStart;

performance.getEntities()

接口/js/link/图片平均响应时间、慢接口/js/link/图片、FP/FCP...

performance.getEntities()都能做些什么?

同源限制

  • 跨域只有部分数据
  • 设置 "timing-allow-origin" 响应头的资源也算同源

duration包括阻塞时间

缓存相关

其他重要属性

TTI(Time to Interactive)

const ttiPolyfill = require('tti-polyfill');
const tti = await ttiPolyfill.getFirstConsistentlyInteractive();

First CPU Idle

const arr = performance.getEntriesByType('paint');
let data = {};
arr.forEach(item=>{data[item.name] = item.startTime});
  • 当页面被最小化,或者其他方式隐藏时,浏览器没有绘制,取不到值
  • duration始终为0
  • navigation最为起始时间,也就是说 fp/fcp 的 startTime 是相对于performance.getEntriesByType('navigation').startTime的时间

FP/FCP

FP(first paint):首次绘制,第一次看到白屏的时间

FCP(first contentful paint):首次内容绘制,元素首次绘制的时间点

Long task(实验性)

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // 上报
  }
});

observer.observe({entryTypes: ['longtask']});

执行时间 >= 50ms 的任务

二娃

前端性能监控 sdk,基于神策

// npm install erwa --save --registry=https://npm.firstleap.cn


import Erwa from 'erwa';

// 传入项目名称

const erwa = new Erwa('LeapFlow', {
  urlReg: /^https?:\/\/leapflow.firstleap.cn/
});

性能优化

  • 加载优化
  • 渲染优化

加载优化-减小代码体积

  • 源码压缩
  • gzip
  • 按需加载、code split
  • 移除未使用的代码、tree shaking

加载优化-缓存

  • http缓存
  • 本地缓存
  • Service Worker
  • CDN

加载优化-图片

 

  • 压缩,裁剪
    • 有损压缩
    • 无损压缩
  • 精灵图
  • 预加载
  • 视频替换gif
  • 延迟加载
  • CDN
  • 选取合适格式的图片
    • svg
    • webp
    • Baseline JPEG、Progressive JPEG


<picture>
  <source 
  srcset="https://file-aliyun.firstleap.cn/test.png?x-oss-process=image/format,webp" 
  type="image/webp">
  <img src="https://file-aliyun.firstleap.cn/test.png" alt="">
</picture>


// 加载图片时
function load(src){
  if(isSupportWebp){
  // 加载webp

  }else{
  // 加载原图片
  }
}

加载优化-其他

  • 多域名
  • 字体优化
  • http/2
  • ... ...

渲染优化

 

  • 动画用requestAnimationFrame
  • 耗时任务用web worker
  • 避免频繁更改样式(class vs style)
  • 避免重排,重绘
  • 硬件加速
  • 频繁操作用Debounce

避免性价比低的性能优化

Thanks

前端性能杂谈

By Qingxin Zheng

前端性能杂谈

前端性能杂谈

  • 2,740