前言
性能优化
一直是我们需要注意的问题,尤其是对于C端
产品来说更是重中之重。而关于性能优化
的文章教材层出不穷,这里不过多赘述。而对于性能优化过后,性能到底提升了多少,我们需要有一个很直观的数据,才能更加方便我们工作,也能更清楚的计算出我们所获得的成果。
效果展示:
源码地址: pmat
现有工具
现在的性能检测方式非常多
- Chrome 自带的开发者工具:Performance
- Lighthouse 开源工具
- 原生 Performance API
- 各种官方库、插件
这些工具都各有各的优点,但同样都有一定的局限性。比如 Lighthouse
可以用可视化各类指标来非常直观的看到各项检测数据,但是却无法完成一些特定的需求,比如有的项目需要登录才能看到具体的页面等等...
而我们需要做的是,结合他们的一些特点,整合成我们所需要的东西,而且对于甚至不了解原生 Performance API
这块知识的同学来说,是个非常好的学习跟实践的机会。
准备知识
这里在开始做工具之前,需要了解原生 Performance API
知识点。
这里直接推荐 2018你应该知道的Web性能信息采集指南, 虽然时间有些久了,但看完基本上对这块已经有了些基本了解,这里就不赘述了。
想要什么样的工具
这里表明下我的思路其实就是结合了社区大佬写的 per-moniteur 和 hiper
per-moniteur
: 项目内注入js
, 利用PerformanceObserver
监听页面性能,在控制台输出结果,主要监测FCP
,LCP
等性能指标hiper
: 命令行工具,利用puppeteer
启动无头浏览器多次启动输入网址即可返回平均
监测数据, 不过只计算了performance.timing
(已过时)的数据
所以我的想法就是将它两者结合起来,即只要输入网址和请求次数,即可测试到它的性能指标(包括基础的检测数据以及性能指标数据),也可根据puppeteer
扩展缓存设置等配置。
开始
工具全部使用typescript
开发,发布前转换成commonjs
语法,这也没有什么特别,基本所有人都会。
○ 开始入口
class Pmat {
// 命令入口,解析参数
public cli: Cli;
// puppeteer 启动无头浏览器
public puppeteer: Puppeteer;
// 性能检测对象数组
public observer: Observer;
constructor() {
this.cli = new Cli();
this.puppeteer = new Puppeteer();
this.observer = new Observer();
}
async run() {
// 获取命令返回的参数
const options = await this.cli.monitor();
// 初始化无头浏览器
const puppeteer = await this.puppeteer.init(options);
......
const { count, url } = options;
const { page, browser } = puppeteer;
// listr2 创建任务
const task = new Listr([
{
title: 'start executing',
task: async () => {
// 根据输入的打开的页面次数,进行检测
for (let i = 0; i < count; i += 1) {
// 执行生命周期 beforeStart
await this.observer.beforeStart();
await page.goto(url, { waitUntil: 'load' });
// 开始检测
await this.observer.start();
}
},
},
{
title: 'start calculating',
task: async () => {
// 根据检测的值进行计算,获取平均数
await this.observer.calculate();
},
},
]);
......
}
}
上面代码就是启动入口的操作,即根据命令的输入参数进行相应的操作。
○ 创建无头浏览器
import puppeteer from 'puppeteer';
import type { IPuppeteerOutput } from './interface';
import type { ICliOptions } from '../cli/interface';
class Puppeteer {
async init(options?: ICliOptions): Promise<IPuppeteerOutput> {
const browser = await puppeteer.launch({
product: 'chrome',
});
const page = await browser.newPage();
// 获取命令行参数
const { cache, javascript, online, useragent, tti } = options;
// 设置每个请求忽略缓存
await page.setCacheEnabled(cache);
// 是否启用js
await page.setJavaScriptEnabled(javascript);
// 是否启用离线模式
await page.setOfflineMode(!online);
if (useragent) {
await page.setUserAgent(useragent);
}
return { page, browser, tti };
}
}
export default Puppeteer;
这里就是简单的使用 puppteer
创建无头浏览器,并根据命令行输入的内容设置浏览器属性。更多api
的使用可以参照 官方文档
计算性能
○ 计算 navigation
navigation
是指通过 Performance
接口获取的关于浏览器文档事件的指标的方法和属性。具体方法是 performance.getEntriesByType('navigation')
, 便可获得类似以下格式的数据:
而其中具体的计算方式可以参考:
通过以上知识,就可以很简单地计算出我们想要的数据,计算方式如下:
总时长:duration,
Redirect: redirectEnd - redirectStart,
AppCache: domainLookupStart - fetchStart,
DNS: domainLookupEnd - domainLookupStart,
TCP: connectEnd - connectStart,
First Byte time: responseStart - requestStart,
Download: responseEnd - responseStart,
白屏时间: domInteractive - fetchStart,
DOMReady: domContentLoadedEventEnd - fetchStart,
Load: domContentLoadedEventEnd - domContentLoadedEventStart
注意:因为工具是多次请求一个地址,所以需要计算所有数据总和的平均值
○ 计算性能指标
为了能够更具体的量化性能优化这一块,我们需要获取谷歌提出的这一系列性能指标,但由于谷歌一直在更新性能优化的指标,以下的思维导图算是目前最新的一些性能指标,其中一些性能指标的计算不一定完全准确,只供参考,更详细的内容可以看还在看那些老掉牙的性能优化文章么?这些最新性能指标了解下
而我们如何通过代码去计算这些性能指标呢,主要是通过 PerformanceObserver
来获取数据,可以把它看成是一个监听器用来收集所需要的性能指标,主要格式如下所示:
const perfObserver = new PerformanceObserver((entryList) => {
// 信息处理
})
// 传入需要的 type
perfObserver.observe({ entryTypes: ['paint'] })
FP & FCP
-
FP(First Paint),首次绘制,这个指标用于记录页面第一次绘制像素的时间。
-
FCP(First Contentful Paint),首次内容绘制,这个指标用于记录页面首次绘制文本、图片、非空白 Canvas 或 SVG 的时间。
page.evaluateOnNewDocument(getPaint);
function getPaint() {
window.FP = 0;
window.FCP = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const { startTime, name } = entry;
if (name === 'first-contentful-paint') {
window.FCP = startTime;
} else {
window.FP = startTime;
}
}
});
observer.observe({ entryTypes: ['paint'] });
}
LCP
- LCP(Largest Contentful Paint),最大内容绘制,用于记录视窗内最大的元素绘制的时间
await page.evaluateOnNewDocument(calcLCP);
await page.goto(url, { waitUntil: 'load', timeout: 60000 });
let lcp = await page.evaluate(() => {
return window.largestContentfulPaint;
});
function calcLCP() {
window.largestContentfulPaint = 0;
const observer = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
window.largestContentfulPaint = lastEntry.renderTime || lastEntry.loadTime;
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
observer.takeRecords();
observer.disconnect();
console.log('LCP:', window.largestContentfulPaint);
}
});
}
CLS
- CLS(Cumulative Layout Shift),累计位移偏移,记录了页面上非预期的位移波动。
window.CLS = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
window.CLS += entry.value;
}
}
});
observer.observe({ entryTypes: ['layout-shift'] });
TTI
- TTI(Time to Interactive),首次可交互时间。通俗来讲就是页面从
FCP
到可以点击交互的时间,这个指标非常的重要,基本上就标志了页面的性能。而它的计算需要符合下面的几个条件:
- 从
FCP
指标后开始计算 - 持续 5 秒内无长任务(执行时间超过 50 ms)且无两个以上正在进行中的
GET
请求 - 往前回溯至 5 秒前的最后一个长任务结束的时间
TTI
的计算使用了 tti-polyfill
:
window.__tti = { e: [] };
const observer = new PerformanceObserver((list) => {
const fcp = performance.getEntriesByName('first-contentful-paint')[0].startTime;
const entries = list.getEntries();
window.__tti.e = window.__tti.e.concat(entries);
})
observer.observe({ entryTypes: ['longtask'] });
...
await page.addScriptTag({ path: './node_modules/tti-polyfill/tti-polyfill.js' });
// Time to Interactive
TTI = await page.evaluate(() =>
window.ttiPolyfill ? window.ttiPolyfill.getFirstConsistentlyInteractive() : -1,
);
FID
- FID(First Input Delay),首次输入延迟,记录在 FCP 和 TTI 之间用户首次与页面交互时响应的延迟。
window.FID = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
window.FID = entry.processingStart - entry.startTime;
}
});
observer.observe({ type: 'first-input', buffered: true });
TBT
- TBT(Total Blocking Time),阻塞总时间,记录在 FCP 到 TTI 之间所有长任务的阻塞时间总和。
window.TBT = 0;
const observer = new PerformanceObserver((list) => {
const fcp = performance.getEntriesByName('first-contentful-paint')[0].startTime;
const entries = list.getEntries();
for (const entry of entries) {
if (entry.name !== 'self' || entry.startTime < fcp) {
return;
}
// long tasks mean time over 50ms
const blockingTime = entry.duration - 50;
if (blockingTime > 0) window.TBT += blockingTime;
}
});
observer.observe({ entryTypes: ['longtask'] });
最后
平时空闲的时候写了这个工具,一是为了学习性能检测方面的知识点,二是为了在之后对项目进行性能优化的时候能够更直观的看到优化的幅度。有问题的地方欢迎指正。感谢。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!