前端性能优化之Lighthouse分数为什么上不去?

年底了冲一波绩效。走过路过的朋友们点点高贵的小手手,帮我的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

相关推荐
我要洋人死3 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人15 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人15 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR21 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香23 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969325 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai31 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_91540 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#