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

相关推荐
MiyueFE8 分钟前
14 个逻辑驱动的 UI 设计技巧,助您改善任何界面
前端·设计
啃火龙果的兔子12 分钟前
前端单元测试覆盖率工具有哪些,分别有什么优缺点
前端·单元测试
「、皓子~40 分钟前
后台管理系统的诞生 - 利用AI 1天完成整个后台管理系统的微服务后端+前端
前端·人工智能·微服务·小程序·go·ai编程·ai写作
就改了43 分钟前
Ajax——在OA系统提升性能的局部刷新
前端·javascript·ajax
凌冰_1 小时前
Ajax 入门
前端·javascript·ajax
京东零售技术1 小时前
京东小程序JS API仓颉改造实践
前端
老A技术联盟1 小时前
从小白入门,基于Cursor开发一个前端小程序之Cursor 编程实践与案例分析
前端·小程序
风铃喵游1 小时前
构建引擎: 打造小程序编译器
前端·小程序·架构
sunbyte1 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)
前端·javascript·css·vue.js·前端框架·tailwindcss
小飞悟1 小时前
🎯 什么是模块化?CommonJS 和 ES6 Modules 到底有什么区别?小白也能看懂
前端·javascript·设计