Skip to content

首屏加载优化

首页加载指标细化

加载性能:

  1. FP(First Paint 首次绘制),取决于后端返回内容的响应速度或者CDN的加载速度。
  2. FCP(First Contentful Paint 首次内容绘制),FP 到 FCP 中间主要是 SPA 应用执行JS脚本所耗的时间,太慢就会白屏时间过长。
  3. LCP(Largest Contentful Paint 最大内容绘制),加载最大内容快呈现时间。
  4. TTFB(Time to First Byte):首字节到达时间,请求发出后接收到数据中间的时间。
  5. FMP (First Meaningful Paint 首次有效绘制),主要内容呈现的时间(MutationObserver)

交互性能:

  1. INP(Interactive to Next Paint 用户互动时间),用户可交互的时间。
  2. TTI(Time to Interactive 可交互时间),SSR优化。
  3. TBT(Total Blocking Time 阻塞时间从 FCP 到 TTI):总阻塞时间。
  4. CLS(Cumulative Layout Shift 累计布局偏移):布局偏移情况,重排 reflow。

性能指标获取

  1. 开发阶段工具
    • Chrome DevTools:Performance 面板用于捕获FP、FCP、LCP等指标。
    • Lighthouse:生成页面性能报告,提供优化建议。
    • web-vitals:监控FCP、LCP、CLS等核心Web Vitals指标。
  2. 生产环境监控
    • Google Analytics:配置自定义事件记录指标。
    • Web Performance API:直接从浏览器获取性能数据。
  3. 性能监控平台:
    • 使用开源平台(如Prometheus、Grafana)或付费平台(如New Relic、Datadog)
  4. 用户行为数据采集
    • 埋点记录页面加载时间、交互延迟等关键性指标,结合用户行为分析优化方向。

指标计算

通过 Performance APIweb-vitals 库采集核心指标

js
const paintEntries = performance.getEntriesByType('paint');
paintEntries.forEach((entry) => {
   const { name, startTime } = entry
   console.log(name, startTime)
})
js
// PerformanceObserver
const LCPObserver = new PerformanceObserver((list) => {
   const entries = list.getEntries();
   console.log('LCP:', entries.startTime);
});
LCPObserver.observe({ type: 'largest-contentful-paint', buffered: true });

// web-vitals
import { getLCP } from 'web-vitals';
getLCP((metric) => {
   console.log('LCP:', metric.value);
   reportMetricToServer('LCP', metric.value);
});
js
// PerformanceObserver
let clsValue = 0
const CLSObserver = new PerformanceObserver((list) => {
   list.getEntries().forEach((entry) => {
      if (!entry.hadRecentInput) {
         clsValue += entry.value
      }
   })
})
CLSObserver.observe({ type: 'layout-shift', buffered: true });
window.addEventListener('beforeunload', () => {
   console.log('CLS:', clsValue)
})
// web-vitals
import { getCLS } from 'web-vitals';
getCLS((metric) => {
   console.log('CLS:', metric.value);
   reportMetricToServer('CLS', metric.value);
});
js
// 获取长任务
// PerformanceObserver
const TBTObserver = new PerformanceObserver((list) => {
   const longTasks = list.getEntries().filter(entry => entry.duration > 50);
   // 超过50ms才算阻塞
   longTasks.forEach(task => {
      console.log('TBT:', task.duration)
   })
})
TBTObserver.observe({ type: 'longtask', buffered: true });
// web-vitals
import { getFID } from 'web-vitals';
getFID((metric) => {
   console.log('INP:', metric.value);
   reportMetricToServer('INP', metric.value);
})
js
// 监听用户交互并计算延迟时间
const INPObserver = new PerformanceObserver((list) => {
   list.getEntries().forEach(entry => {
      console.log('INP:', entry.interactionStart, entry.processingStart - entry.interactionStart)
   })
});
INPObserver.observe({ type: 'event', buffered: true });
js
function reportMetricToServer(metricName, metricValue) {
   fetch('/api/report-metric', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ metricName, metricValue, timestamp: Date.now() })
   })
}

方案

  1. 优化图片,webp,图片压缩、图片尺寸(在合适的容器内用合适的尺寸图片)
  2. 字体瘦身,设计型产品,字体子集化(用了哪些字,就最后只生成对应字的字体文件)可参考FontMin库
  3. 懒加载资源,图片懒加载,js 异步加载
  4. css、js 文件压缩,打包构建阶段完成(terser、esbuild)
    • 代码压缩
    • 文件合并
    • 文件拆分
    • Tree shaking
    • 动态加载
  5. Gzip、Brotli 压缩
  6. SSR、SSG静态生产方案

动画卡顿

为什么会卡顿,因为JS是单线程,阻塞线程,导致卡顿。

方案

  1. 减少主线阻塞
    • 优化 JavaScript 执行,较少长任务(复杂的计算【web worker、将任务切分(react Scheduler)】)
  2. GPU加速
    • css 属性(transform、opacity)
    • 避免会引起重排的属性(定位 left、top)
  3. requestAnimationFrame,cpu空闲时候抢占执行任务
  4. 节流、防抖

应用层状态优化

  1. 减少全局状态的依赖
    • 将状态尽可能局部化,避免使用全局状态(如 Redux 或 Context)管理所有数据。
    • 示例:对于仅用于某些组件的状态,可以使用组件的 useState 或 useReducer 来管理,而不是全局状态。
  2. 优化 Context 的性能
    • Context 的更新会重新渲染所有订阅的组件。
    • 解决方案:拆分 Context,将不同的逻辑存储在多个 Context 中,降低重新渲染范围。
  3. 使用高效的状态管理库
    • 使用轻量、高性能的状态管理工具,如 Zustand、Jotai 或 Recoil,它们具备更细颗粒度的状态更新机制。
  4. 避免不必要的状态更新