性能优化篇-优化首屏体验

676 阅读6分钟

首屏体验指标

以用户为中心的性能指标

  • First Paint 首次绘制(FP)
  • First contentful paint 首次内容绘制 (FCP)
  • Largest contentful paint 最大内容绘制 (LCP)
  • First input delay 首次输入延迟 (FID)
  • Time to Interactive 可交互时间 (TTI)
  • Total blocking time 总阻塞时间 (TBT)
  • Cumulative layout shift 累积布局偏移 (CLS)

FP & FCP

首次绘制,FP(First Paint),这个指标用于记录页面第一次绘制像素的时间。

首次内容绘制,FCP(First Contentful Paint),这个指标用于记录页面首次绘制文本、图片、非空白 Canvas 或 SVG 的时间。

FCP时间颜色分数评级
0-1.8秒绿色75~100分良好
1.9-3.0秒橙色50~74分需要改进
3.1秒以上红色0~49分较差

LCP

最大内容绘制,LCP(Largest Contentful Paint),用于记录视窗内最大的元素绘制的时间,该时间会随着页面渲染变化而变化,因为页面中的最大元素在渲染过程中可能会发生改变,另外该指标会在用户第一次交互后停止记录。

LCP时间颜色分数评级
0-2.5秒绿色75~100分良好
2.6-4.0秒橙色50~74分需要改进
4.1秒以上红色0~49分较差

FID

首次输入延迟,FID(First Input Delay),记录在 FCP 和 TTI 之间用户首次与页面交互时响应的延迟。

这个指标其实挺好理解,就是看用户交互事件触发到页面响应中间耗时多少,如果其中有长任务发生的话那么势必会造成响应时间变长。

FID时间颜色分数评级
0-100毫秒绿色75~100分良好
101-300毫秒橙色50~74分需要改进
300毫秒以上红色0~49分较差

CLS

累计位移偏移,CLS(Cumulative Layout Shift),记录了页面上非预期的位移波动。

大家想必遇到过这类情况:页面渲染过程中突然插入一张巨大的图片或者说点击了某个按钮突然动态插入了一块内容等等相当影响用户体验的网站。这个指标就是为这种情况而生的,计算方式为:位移影响的面积 * 位移距离

CLS颜色分数评级
0-0.1绿色75~100分良好
0.1-0.25橙色50~74分需要改进
> 0.25红色0~49分较差

Google 三大核心指标

Google 提出了网站用户体验的三大核心指标,分别为:

  • LCP
  • FID
  • CLS

LCP 代表了页面的速度指标,虽然还存在其他的一些体现速度的指标,但是上文也说过 LCP 能体现的东西更多一些。一是指标实时更新,数据更精确,二是代表着页面最大元素的渲染时间,通常来说页面中最大元素的快速载入能让用户感觉性能还挺好。

FID 代表了页面的交互体验指标,毕竟没有一个用户希望触发交互以后页面的反馈很迟缓,交互响应的快会让用户觉得网页挺流畅。

CLS 代表了页面的稳定指标,尤其在手机上这个指标更为重要。因为手机屏幕挺小,CLS 值一大的话会让用户觉得页面体验做的很差。

如何获取指标

官方出品,可以通过安装 web-vitals-extension 插件来获取三大核心指标

web-vitals 库

官方出品,你可以通过安装 web-vitals 包来获取如下指标

import {getCLS, getFID, getLCP} from 'web-vitals';  
  
getCLS(console.log);  
getFID(console.log);  
getLCP(console.log);  

Lazy-Load 初探

掘金首页采用了懒加载策略。当我们的页面并未滚动至包含图片的 div 元素所在的位置时,它的样式是这样的:

<div data-v-b2db8566=""  
    data-v-009ea7bb=""  
    data-v-6b46a625=""  
    data-src="https://user-gold-cdn.xitu.io/2018/9/27/16619f449ee24252?imageView2/1/w/120/h/120/q/85/format/webp/interlace/1"  
    class="lazy thumb thumb"  
    style="background-image: none; background-size: cover;">  
</div>
  • 我们注意到 style 内联样式中,背景图片设置为了 none。也就是说这个 div 是没有内容的,它只起到一个占位的作用。
  • 一旦我们通过滚动使得这个 div 出现在了可见范围内,那么 div 元素的内容就会发生变化,呈现如下的内容:
style="background-image: url(&quot;https://user-gold-cdn.xitu.io/2018/9/27/16619f449ee24252?imageView2/1/w/120/h/120/q/85/format/webp/interlace/1&quot;); background-size: cover;"

一起写一个 Lazy-Load

<!DOCTYPE html>  
<html lang="en">  
<head>  
<meta charset="UTF-8">  
<meta >  
<meta http-equiv="X-UA-Compatible" content="ie=edge">  
<title>Lazy-Load</title>  
<style>  
    .img {  
        width: 200px;  
        height:200px;  
        background-color: gray;  
    }  
    .pic {  
        // 必要的img样式  
    }  
</style>  
</head>  
<body>  
    <div class="container">  
        <div class="img">  
            // 注意我们并没有为它引入真实的src  
            <img class="pic" alt="加载中" data-src="./images/1.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/2.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/3.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/4.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/5.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/6.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/7.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/8.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/9.png">  
        </div>  
        <div class="img">  
            <img class="pic" alt="加载中" data-src="./images/10.png">  
        </div>  
    </div>  
</body>  
</html>
  • 在懒加载的实现中,有两个关键的数值:一个是当前可视区域的高度,另一个是元素距离可视区域顶部的高度
  • 当前可视区域的高度, 在和现代浏览器及 IE9 以上的浏览器中,可以用 window.innerHeight 属性获取。在低版本 IE 的标准模式中,可以用 document.documentElement.clientHeight 获取,这里我们兼容两种情况:
const viewHeight = window.innerHeight || document.documentElement.clientHeight

元素距离可视区域顶部的高度,我们这里选用 getBoundingClientRect() 方法来获取返回元素的大小及其相对于视口的位置。对此 MDN 给出了非常清晰的解释:

返回的DOMRect 对象包含了一组用于描述边框的只读属性———lefttopright 和 bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。

截屏2023-08-28 下午10.42.18.png

Lazy-Load 方法的实现:

<script>  
    // 获取所有的图片标签  
    const imgs = document.getElementsByTagName('img')  
    // 获取可视区域的高度  
    const viewHeight = window.innerHeight || document.documentElement.clientHeight  
    // num用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出  
    let num = 0  
    function lazyload(){  
        for(let i=num; i<imgs.length; i++) {  
            // 用可视区域高度减去元素顶部距离可视区域顶部的高度  
            let distance = viewHeight - imgs[i].getBoundingClientRect().top  
            
            // 如果可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出  
            if(distance >= 0 ){  
                // 给元素写入真实的src,展示图片  
                imgs[i].src = imgs[i].getAttribute('data-src')  
                // 前i张图片已经加载完毕,下次从第i+1张开始检查是否露出  
                num = i + 1  
            }  
        }  
    }  
    // 监听Scroll事件  
    window.addEventListener('scroll', lazyload, false);  
</script>