前端性能优化之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

相关推荐
爱勇宝14 小时前
鸿蒙生态的下半场:开发者不只要能开发,还要能赚钱
android·前端·程序员
IT_陈寒17 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
kyriewen17 小时前
我用 AI 一周写完了整个项目,上线第一天就崩了——这是我踩过最贵的 5 个坑
前端·javascript·ai编程
牧艺18 小时前
从零到协同:构建类飞书在线文档系统的五个技术重难点
前端·人工智能
红尘散仙19 小时前
想写一个像样的终端 App?试试把 React 的开发体验搬进 Rust TUI
前端·rust
袋鼠云数栈UED团队19 小时前
一套 Spec-First 的 AI 编程工作流
前端·人工智能
袋鼠云数栈前端19 小时前
一套 Spec-First 的 AI 编程工作流
前端·ai+
angerdream19 小时前
Android手把手编写儿童手机远程监控App之vue3 路由守卫
前端
不服老的小黑哥20 小时前
AI规范驱动编程-harness工程项目实战
前端
vivo互联网技术20 小时前
从 Web 到桌面:基于 Tauri 2.0 + Vue 3 打造 vivo 线下门店「大头贴」拍照体验系统
前端·rust