# 概述

# 性能优化步骤
针对前端全链路性能优化专题,我们依次做如下几个关键步骤
- 确定优化范围和关键路径 
- 拆开并定义关键指标 
- 计算、收集、分析指标数据 
- 执行具体优化方案 
- 对比分析调整优化结果 
- 灰度测试并完善数据指标 
- 输出优化后完整的数据报表 
# 关键指标的设定
- 自动化检测
- 通过Lighthouse
- Performance 面板
- Chrome DevTools Protocol
 
- 手动检测 - Performance API
LCP: 加载性能。最大内容绘制应在 2.5s 内完成。
FID: 交互性能。首次输入延迟应在 100ms 内完成。
CLS: 页面稳定性。累积布局偏移,需手动计算,CLS 应保持在 0.1 以下。
# 加载
- 首屏时间=DOMContentLoaded 时间=domContentLoadedEventEnd-fetchStart 
- 网络环境采集 
两张不同尺寸的图片 下载时间/文件体积 求平均值
- 弱网优化
白屏
- 白屏时间 FP= domLoading-navigationStart
# 交互
- 手动录制
- 自动化用 adb 录制视频;adb shell screenrecord --time--limt 10/sdcard/perf.mp4
- 白屏响应时间=白屏最后- 帧的时间- 点击时的起始帧时间
- 首屏加载时间=内容完全加载出来那一帧的时间-点击时的起始帧时间
- 弱网环境测试
- 2G/3G/4G 各种特殊场景测试
- 采用分帧计算
# 渲染
# 构建
- 通过 Webpack speed-measure-webpack-plugin 插件测量构建时间
拿到各阶段的loader和plugin插件执行占用时间
- 通过 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 (预加载):提前加载后面会用到的关键资源 ⚠️ 因为会提前拉取资源,如果不是特殊需要,谨慎使用
- 路由懒加载,无需加载整个应用的资源 
- 降低白屏时间: 
- DNS 走缓存
- 开启 Dns 预解析 x-dns-prefetch-control
- dns-prefetch 强制对 域名做预解析
- 端内:提前初始化 WebView 并且加载域名开始解析,端外:iframe
- 使用骨架屏幕, 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 
- 降低卡顿: 
- 数据相关,找后端协调 
- 主线程和合成线程调度问题;合成线程主要用于绘制 利用 transform 
- 空间换时间,时间换空间 ,复杂任务拆分为多个任务 
- 在对 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