年底了冲一波绩效。走过路过的朋友们点点高贵的小手手,帮我的mini-react冲上400。
前言
我们一般都是用谷歌浏览器自带的Lighthouse
测试网站的性能。但有时候我们明明已经做了很多优化,并且各项指标已经降下来了,可Lighthouse
分数却一直没上去,这是为什么?比如下面图示
优化前:
优化后:
优化后,FCP、LCP、SI这三个指标的下降幅度非常明显,优化力度很大,但为什么分数还是26呢?
实际上,这和Lighthouse
指标计算有关。下面,让我们展开说说
大纲
- Lighthouse分数计算
- 实例讲解Lighthouse指标
- 性能预警:如何使用脚本跑分,并实现鉴权
Lighthouse分数计算
可以从下图所示的地方进去Lighthouse分数计算规则页面。
Lighthouse分数计算规则页面
从上图至少可以得出以下几点结论
- 每个指标的权重不同。总分是根据这些指标的得分加权计算而来的。
- 每个指标的耗时都有最大值,最小值都是0。这个可以从拖动滑杆得知。
- FCP最大耗时为4000ms。4000ms以及以上,得分为0
- SI最大耗时为5000ms。5000ms以及以上,得分为0
- LCP最大耗时为6000ms。6000ms以及以上,得分为0
- TBT最大耗时为2000ms。2000ms以及以上,得分为0
- CLS最大为0.82。0.82以及以上,得分为0。
- 各指标如果都是0,则总分满分。表示性能最好
在上面的例子中,优化前
- FCP为10.2秒,远超4000ms,得分为0。
- LCP为10.2秒,远超6000ms,得分同样为0。
优化后
- FCP为6.0秒,减少了4秒,但还是大于4000ms,因此得分还是0
- LCP为6秒,刚好达到最大值,因此得分还是0
因此,虽然我们优化后,各指标下降很明显,视觉上我们的网页显示速度也快了很多,但由于各指标还是超过了最大值,得分依旧是0,加权后总分还是不变。
Lighthouse指标实验
FCP && LCP
以下面的代码为例
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
const startTime = Date.now();
while (Date.now() - startTime < 9000) { }
</script>
<body>
<div id="root">
hello world
<button>btb</button>
</div>
</body>
</html>
这里我们阻塞主线程9秒钟,跑分如下:
如果我们修改阻塞时间,改成6秒
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
const startTime = Date.now();
while (Date.now() - startTime < 6000) { }
</script>
<body>
<div id="root">
hello world
<button>btb</button>
</div>
</body>
</html>
跑分如下:
从2个demo可以看到,FCP和LCP都下降很明显,但是我们的分数还是没有提高。但页面显示速度明显变快了。
如果将阻塞时间调成500ms
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
const startTime = Date.now();
while (Date.now() - startTime < 500) { }
</script>
<body>
<div id="root">
hello world
<button>btb</button>
</div>
</body>
</html>
跑分如下:
TBT
这里我们通过在setTimeout里添加一个while循环以实验TBT耗时。
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<body>
<div id="root">
hello world
<button>btb</button>
</div>
<script>
setTimeout(() => {
const startTime = Date.now();
while (Date.now() - startTime < 6000) { }
}, 0);
</script>
</body>
</html>
可以看出,FCP和LCP都已经几乎满分,TBT耗时较大。
可以尝试着调整setTimeout里面while循环的时间,并跑分观察TBT耗时。
CLS
html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>test</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
.block {
width: 5px;
height: 5px;
background-color: red;
}
</style>
<body>
<div id="root">
hello world
<div id="block2" class="block">
</div>
<p>dsafdsd </p>
<div id="block" class="block">
</div>
<button>btb</button>
<p>
hello world
</p>
</div>
<script>
let size = 5;
const resize = (el) => {
setTimeout(() => {
size = size + 5;
el.style.width = `${size}px`;
el.style.height = `${size}px`;
if (size < 1000) {
resize(el)
}
}, 1);
}
resize(block)
resize(block2)
</script>
</body>
</html>
工程化跑分
我们可以使用cicd结合node服务,提供性能预警。比如我们可以在开发阶段,提交代码触发CICD流水线,然后自动化跑分,再通过企微机器人推送到开发。这样在开发阶段,就能很容易把控每次commit对性能的影响有多大,就可以在开发阶段优化我们的代码逻辑。
使用谷歌提供的lighthouse实现自动化跑分,代码如下:
js
import lighthouse from 'lighthouse';
import * as chromeLauncher from 'chrome-launcher';
async function runLighthouse(url) {
const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] });
const options = { logLevel: 'info', output: 'html', onlyCategories: ['performance'], port: chrome.port };
const runnerResult = await lighthouse(url, options);
await chrome.kill();
return runnerResult;
}
const url = 'http://localhost:8080/';
runLighthouse(url, {})
.then(runnerResult => {
// report是个html
delete runnerResult.report
const score = runnerResult.lhr.categories.performance.score * 100;
const fcp = runnerResult.lhr.audits['first-contentful-paint'].displayValue;
const lcp = runnerResult.lhr.audits['largest-contentful-paint'].displayValue;
const tbt = runnerResult.lhr.audits['total-blocking-time'].displayValue;
const cls = runnerResult.lhr.audits['cumulative-layout-shift'].displayValue;
const si = runnerResult.lhr.audits['speed-index'].displayValue;
console.log({ score, fcp, lcp, tbt, cls, si });
})
.catch(error => {
console.error(error);
});
如果页面需要鉴权,可以参考lighthouse-authenticated-pages.md