关于前端:我给网站做了一场性能手术

33次阅读

共计 3610 个字符,预计需要花费 10 分钟才能阅读完成。

前言

风和日丽: 我正笑嘻嘻地抓着我炫酷的 ikbc 键盘疯狂的敲着 Bug.
晴天霹雳: 被拉进了一个群, 产品说我做的网站很卡, 须要做性能优化.
难以置信: 我可是用尊贵的 Vue3+Ts 开发的呢 (手动狗头).
非常抗拒: 迫于 yin 威, 我给网站做了体检和手术.

体检

市面上的体检套餐有很多种, 但其实都是换汤不换药. 那药 (规范) 是什么呢? 咱们会在上面阐明. 这里我抉择了谷歌亲儿子 ” 灯塔 ”(LightHouse)进行性能体检.

体检标准

为什么我说大多体检套餐都是换汤不换药呢? 咱们先从灯塔的计分规定说起:

从下面中咱们能够看到灯塔 v6/v7 版是通过几种性能指标及不同权重来进行计分的. 这几种指标次要是依据 PerformanceTiming 和 PerformanceEntry API 规范进行定义. 市面上大多体检套餐也是基于这些指标定制的. 接下来咱们来理解下这些指标的含意吧.

FCP (First Contentful Paint)

渲染第一个元素 (文本、图片、canvas…) 的工夫点

SI (Speed Index)

首屏展示工夫

LCP (Largest Contentful Paint)

渲染可视区域内最大内容元素的工夫点

TTI (Time to Interactive)

页面资源加载胜利并能响应用户交互的工夫点

TBT (Total Blocking Time)

FCP 到 TTI 之间, 主线程被 long task(超过 50ms)阻塞的工夫之和

CLS (Cumulative Layout Shift)

累计布局偏移值

FID (First Input Delay)

用户第一次在页面进行交互(点击链接、按钮、自定义 js 事件), 到浏览器理论开始解决这个事件的工夫

Core Web Vitals

谈到用户体验与性能指标, 顺便提下 Core Web Vitals.
2020 年 5 月,Google 针对网站应用体验推出了一套外围指标规范(Core Web Vitals). 由三项指标形成:

为什么不是别的指标呢 ? 因为这套规范次要从以下三个维度进行评估:

  • [加载状况] : LCP
  • [交互性] : FID
  • [视觉稳定性] : CLS

    如何检视 Core Web Vitals 指标 ?

    开发者可利用以下几种工具对 Core Web Vitals 进行监测:

因为 FID 须要一个实在用户的交互, 所以无奈用试验数据测试. 为了能在试验数据下测试 FID, 通常会用 TBT (Total Blocking Time). 尽管他们测量的内容不同, 但改善 TBT 通常也能改善 FID.

体检后果

不检不晓得, 一检吓一跳.6 个 ” 重要器官 ” 凉了一半 … 是时候对它动个手术了!

指标评分

改善倡议

手术

手术计划

既然是性能手术, 计划就次要以性能指标作为维度, 次要分为以下几个点:

  • 视觉稳定性 (Cumulative Layout Shift)
  • 加载状况 (Largest Contentful Paint)
  • TTI (Time to Interactive)
  • TBT (Total Blocking Time)
  • FCP (First Contentful Paint)

手术过程

视觉稳定性 (Cumulative Layout Shift)

  • 优化未设置尺寸的图片元素

    改善倡议里提到了一项优先级很高的优化就是为图片元素设置显式的宽度和高度, 从而缩小布局偏移和改善 CLS.

<img src="hello.png" width="640" height="320" alt="Hello World" />
  • 自定义字体文件加载期间放弃可见状态

    改善倡议里提到应用 CSS font-display 属性确保自定义字体文件在加载期间可见.

这是因为网站下载自定义字体文件须要一段时间, 而不同浏览器此时的行为是不同的. 一些浏览器在加载自定义字体时会暗藏文字, 这种称之为FOIT(Flash Of Invisible Text). 而一些浏览器会显示降级字体, 这种状况称之为FOUT(Flash Of Unstyled Tex). 这两种行为会导致 ” 字体闪动问题 ”, 影响视觉稳定性 (CLS).

我的解决办法是间接设置 font-display:swap; 这个属性能确保字体在加载工夫可见. 尽管还是会引发 FOUT, 然而相比 FOIT,FOUT 对视觉稳定性的影响会小一些.

更好的计划应该是预加载 (preload) 字体文件. 让字体下载有更高概率赶在 FCP 之前, 从而防止 FOIT/FOUT.

@font-face {
     font-family: 'Hello-World';
     src: url('../font/Hello-World.otf') format('OpenType');
     /* swap:如果设定的字体还未可用,浏览器将首先应用备用字体显示,当设定的字体加载实现后替换备用字体 */
     font-display:swap;
 }
  • 防止页面布局产生偏移

咱们产品中有一个顶部动静插入的元素, 这个元素会导致网站整体布局下移. 从而造成了较大的布局偏移. 跟产品及 ui py 交易后, 咱们敌对地对这个元素进行了调整. 将该元素脱离文档流, 采纳固定定位的形式进行展现. 从而解决该问题.

  • 防止非合成动画

改善倡议中提到应防止应用非合成动画, 非合成动画会使得页面变得凌乱并减少 CLS. 对于这个优化倡议我感觉应该具体场景具体分析, 不应该 ” 因噎废食 ”. 毕竟目前能被 composited 的 css 属性只有 transform & opacity. 当然这也在揭示咱们平时在做 CSS 动画时应留神优化 (比方常见的应用 transform 代替 top).

加载状况 (Largest Contentful Paint)

  • 替换最大内容绘制元素

    在改善倡议中, 我发现网站的最大内容绘制元素是谷歌地图中的一个图块元素. 这也难怪 LCP 指标的数据体现不现实了, 起因: 链路过长 – 下载谷歌地图 Js sdk => 初始化谷歌地图 => 绘制 .

于是, 我决定对最大内容绘制元素进行批改, 从而晋升 LCP 工夫. 我喵了一眼 Largest Contentful Paint API 对于该元素类型的定义, 将 ” 指标 ” 锁定到了一个 loading 元素 (绘制成本低: 默认渲染, 不依赖任何条件和判断). 通过我对该元素的尺寸动了手脚后(变大), 该元素胜利 ” 上位 ”.

TBT (Total Blocking Time) / TTI (Time to Interactive)

  • 异步加载谷歌地图 Js sdk
    原先加载谷歌地图 Js sdk 是通过动静增加 script 标签同步加载的. 这样做的毛病其实是很显著的:

    • Google Maps Js sdk 加载机会太晚, 影响 TTI 体现和用户体验.
    • js 引擎占据主线程进行相干 js 执行.
      我的解决计划就是对谷歌地图 Js sdk 进行异步加载. 这里须要留神的是 script async/defer 的区别, 我应用的是 defer 进行异步加载(async 加载结束后会立刻执行, 阻塞主线程, 影响 DOM 解析).
    <script
          src="//maps.googleapis.com/maps/api/js"
          type="text/javascript"
          defer
    ></script>
  • 优化构建 bundle 体积
    查看基于 webpack-bundle-analyzer 生成的体积剖析报告我发现有两个可优化的大产物:
    • lottie 动画库

      站点只有一个动画成果的实现用到该库, 跟产品、ui 又一顿 py 后, 咱们决定就义一点 ” 视觉效果 ”. 移除 lottie library, 改用 CSS3 实现.

    • ant-design-vue 中内置的 momentjs 依赖

      momentjs 的语言包 (locale) 体积十分大, 而站点并无国际化需要. 所以这里我间接应用 webpack IgnorePlugin 对语言包进行疏忽.
      通过优化,bundle 体积 (gizp 前) 由原来的 1.8MB 减小至 1.3MB.

FCP (First Contentful Paint)

网站应用的是 Vue 做的客户端渲染. 这也意味着 FCP 过程会有点 ” 漫长 ”. (初始化 Vue 实例等一系列工作须要占用主线程执行 Js). 这里我 ” 自作聪明 ” 地在 html 文件增加了 ” 通明文本占位符 ”, 抢占 FCP 工夫. 这个骚操作我集体认为有点抖伶俐, 大家能够选择性忽视 …

<div id="app">
   <!-- 占位符 -->
   <p style="color:#fff;">Hello World</p>
</div>

其余

除了针对下面几个指标维度进行优化外, 我还做了几点优化, 这里简略提一下:

  • 优化 DOM 嵌套层级及数量
  • 缩小不必要的接口申请
  • 应用 translate 替换 top 做位移 / 动画

手术后果

说了那么多 ” 废话 ”, 那手术后果到底如何呢 ? 是富丽变身还是 ” 反向一 Q 日神仙 ” 呢 ? 间接上图:

通过上图咱们能够看到各项指标及评分都有质的飞跃, 尽管我 ” 不要脸地截了个最高分 ” (LightHouse 每次评分会有稳定, 实际效果是由原来的 50-70 分涨到了 70-90 分) !!!

结语

平时咱们娓娓而谈的性能优化点往往也是咱们最容易疏忽的点. 性能优化也绝非欲速不达, 须要咱们在日常开发中一直去发现和优化. 路漫漫其修远兮 …

如果你感觉我的文章对你有帮忙, 欢送关注我一起游玩~

正文完
 0