# 概述

性能优化体系

# 性能优化步骤

针对前端全链路性能优化专题,我们依次做如下几个关键步骤

  1. 确定优化范围和关键路径

  2. 拆开并定义关键指标

  3. 计算、收集、分析指标数据

  4. 执行具体优化方案

  5. 对比分析调整优化结果

  6. 灰度测试并完善数据指标

  7. 输出优化后完整的数据报表

# 关键指标的设定

  • 自动化检测
    • 通过Lighthouse
    • Performance 面板
    • Chrome DevTools Protocol
  • 手动检测 - Performance API

LCP: 加载性能。最大内容绘制应在 2.5s 内完成。

FID: 交互性能。首次输入延迟应在 100ms 内完成。

CLS: 页面稳定性。累积布局偏移,需手动计算,CLS 应保持在 0.1 以下。

# 加载

  • 首屏时间=DOMContentLoaded 时间=domContentLoadedEventEnd-fetchStart

  • 网络环境采集

两张不同尺寸的图片 下载时间/文件体积 求平均值

  • 弱网优化

白屏

  • 白屏时间 FP= domLoading-navigationStart

# 交互

  1. 手动录制
  2. 自动化用 adb 录制视频;adb shell screenrecord --time--limt 10/sdcard/perf.mp4
  3. 白屏响应时间=白屏最后- 帧的时间- 点击时的起始帧时间
  4. 首屏加载时间=内容完全加载出来那一帧的时间-点击时的起始帧时间
  • 弱网环境测试
  1. 2G/3G/4G 各种特殊场景测试
  2. 采用分帧计算

# 渲染

# 构建

  1. 通过 Webpack speed-measure-webpack-plugin 插件测量构建时间

拿到各阶段的loader和plugin插件执行占用时间

  1. 通过 Webpack webpack-bundle-analyzer 测量构建体积

拿到各js、css、html等文件大小

# 指标收集

# 收集方式

  • 用PerformanceAPI

  • 使用三方库web-vitals

  • 使用三方库sentry

# 上送方式

  • 通过sendBeacon上报

  • 通过gif

# 上送策略

  • 数据过滤

  • 数据抽样

  • socket 心跳链接,实时上送

  • http 满足30条上送,断点续传

  • 弱网时候先存本地,强网时候立即上送

# 加载过程优化(首屏秒开)

# 网络方面

  • DNS优化

    • NDS缓存
    • DNS预解析(dns-prefetch)
  • 使用http2.0

    • 多路复用,在浏览器可并行发送 N 条请求。
    • 首部压缩,更小的负载体积。
    • 请求优先级,更快的关键请求
  • 使用CDN

  • 利用缓存

    • 强缓存(Last-modefiy)
    • 协商缓存(Etag)
  • 开启Gzip/brotli

# 服务端渲染

  • SSR/SSG

  • PWA离线缓存

  • ServiceWorker

# 懒加载/预加载

  • prefetch

prefetch (预获取):浏览器空闲的时候进行资源的拉取

  • preload

preload (预加载):提前加载后面会用到的关键资源 ⚠️ 因为会提前拉取资源,如果不是特殊需要,谨慎使用

  • 路由懒加载,无需加载整个应用的资源

  • 降低白屏时间:

  1. DNS 走缓存
  2. 开启 Dns 预解析 x-dns-prefetch-control
  3. dns-prefetch 强制对 域名做预解析
  4. 端内:提前初始化 WebView 并且加载域名开始解析,端外:iframe
  5. 使用骨架屏幕, Cli+puppeteer

# Hybrid方面

  • 离线加载

    • 创建预置(Js/css/html)压缩后的离线包
    • 更新diff patch包
  • 预渲染(NSR渲染)

  • WebView预加载

    • native端预初始化Webview组件并隐藏,使用时候直接展示
  • WebView池化

  • IOS使用WKWebview

    • 降低内存占用
  • 弱网优化

    • 接口合并
    • 图片尽量用base64
    • 不自动加载图片,只显示本占位图
  • 根据手机品牌(内存、cpu性能)

    • 优化策略
    • 时间换空间(内存小)
    • 空间换时间(cpu差)

# 交互过程优化

# 网络请求

  • 请求前置

  • 请求合并

  • 请求过滤(防重复)

    • cancelToken
  • 请求资源缓存

    • react-query
    • swr

# 编码优化

  • 利用算法
  • object.freeze
  • 使用多线程Webworker

# 组件加载

  • 组件懒加载
    • 长列表
      • 分页
      • 虚拟滚动
    • 图片懒加载
      • 进入视口后加载:使用IntersectionObserver API
      • 使用新的属性: loading=lazy
  • 组件预加载

# 解决卡顿

FPS 在 60 以上,单帧渲染耗时

fps_compatibility 表示兼容性方面的处理

  • 使用requestanimationframe/requestIdleCallback

  • 使用Webworker

  • 降低卡顿:

  1. 数据相关,找后端协调

  2. 主线程和合成线程调度问题;合成线程主要用于绘制 利用 transform

  3. 空间换时间,时间换空间 ,复杂任务拆分为多个任务

  4. 在对 dom 元素增删对过程中最好在 DocumentFragment 上操作,而不是直接在 Dom 上操作

# 渲染

  • preload/prefetch

  • prerender-spa-plugin 预渲染

  • defer/async

  • 防抖/节流

  • 重绘/回流

# 构建过程优化

# 优化打包体积

  • 压缩

    • gzip压缩/brotli
    • js (terserPlugin)
    • html(html-webpack-plugin)
    • css (optimizeCssAssetsWebpackPlugin)
    • 图片压缩(image-webpack-loader)
  • 剔除无用代码

    • js Tree Shaking: 无用导出将在生产环境进行删除
    • css (MinicssExtractPlugin,PurgessWebpackPlugin)
  • 更小的图片体积

    • webp
    • avif
    • 更合适的尺寸: 当页面仅需显示 100px/100px 大小图片时,对图片进行压缩到 100px/100px
    • 更合适的压缩: 可对前端图片进行适当压缩,如通过 sharp 等
  • 代码分割

    • 路由懒加载( import)
    • bundle spliting
    • 入口点分割(多页应用)
    • 提取公共代码
    • 使用CDN包
  • 借用WASM其他语言开发工具链

# 优化打包速度

  • 缩小打包范围

    • exclude/include
    • alias
    • extensions
    • resolverLoader
    • modules
  • 利用缓存

    • 开启babel-loader缓存
    • cache-loader
    • hard-source-webpack-plugin
  • 编译提速

    • browserlist/babel: 及时更新 browserlist,将会产生更小的垫片体积
    • Esbuild(go)
    • SWC(rust)
  • 开启多进程

    • thread-loader
    • happyPack(弃用)
  • 预编译

    • DllPlugin
    • DllReferencePlugin

# 推荐阅读

写在 2022 的前端性能优化指南 (opens new window)

最全的前端性能定位总结 (opens new window)