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

相关推荐
拼图20927 分钟前
Vue.js开发基础——数据绑定/响应式数据绑定
前端·javascript·vue.js
刘志辉32 分钟前
vue反向代理配置及宝塔配置
前端·javascript·vue.js
星叔1 小时前
ARXML汽车可扩展标记性语言规范讲解
java·前端·汽车
编程老船长1 小时前
第18章 从零开始:春节门联网页设计,用DIV+CSS打造传统与现代的完美融合
前端·css·html
sky.fly1 小时前
HTML5+css3(浮动,浮动的相关属性,float,解决浮动的塌陷问题,clear,overflow,给父亲盒子加高度,伪元素)
前端·css·html
Coisini_甜柚か1 小时前
打字机效果显示
前端·vue3·antv
郑小憨2 小时前
Node.js NPM以及REPL(交互式解释器) 使用介绍(基础介绍 二)
开发语言·前端·javascript·npm·node.js
嚣张农民2 小时前
在 WebSocket 连接中出现错误时,如何处理和捕获错误?
前端·javascript·面试
代码搬运媛2 小时前
前端开发利器:npm 软链接
前端·npm·node.js
周三有雨2 小时前
vue3 + vite 实现版本更新检查(检测到版本更新时提醒用户刷新页面)
前端·vue.js·typescript