← 全部文章

深入理解web性能优化

深入理解web性能优化

文章目录
  1. 为什么要做性能优化
  2. 性能的量化
  3. 业界通用指标
  4. 自定义业务指标
  5. 性能数据上报与统计
  6. 性能数据分析
  7. 宏观视角
  8. 统计视角:
  9. 性能优化的常见抓手
  10. 网络连接阶段
  11. 首次请求
  12. 资源加载
  13. 页面解析
  14. 首屏渲染
  15. 异步数据请求
  16. 数据渲染
  17. 交互就绪
  18. 持续优化
  19. 性能优化常用策略
  20. 短期ROI
  21. 中期ROI
  22. 长期ROI
  23. 如何保持长期的性能稳定
  24. 意识增强
  25. 性能观察
  26. 监督机制
  27. 经验再转化

为什么要做性能优化

其实可以从两个视角来看:

  • 正向视角,性能优化能够带来业务转化,比如:优化页面打开时间,下单转化率提升
  • 负向视角,性能优化能够避免用户流失,比如:性能不佳会持续消磨用户耐心,具体来说就是加载缓慢、交互卡顿、页面白屏等问题会导致用户逐渐流失,同时影响口碑,降低新用户转化。

性能的量化

业界通用指标

一般业界会采用 google 设计的 Core Web Vitals 体系,包括 LCP、INP、CLS 等指标来衡量性能。

Core Web Vitals 页面性能指标

Largest Contentful Paint (LCP):衡量加载性能。为了提供良好的用户体验,应在网页首次开始加载的 2.5 秒内完成 LCP。 Interaction to Next Paint (INP):衡量互动性。为了提供良好的用户体验,网页的 INP 应不超过 200 毫秒。 Cumulative Layout Shift (CLS):衡量视觉稳定性。为了提供良好的用户体验,网页的 CLS 应保持在 0.1 或更低。

为确保大多数用户都能达到这些指标的建议目标值,一个合适的衡量阈值是网页加载时间的第 75 个百分位数,并按移动设备和桌面设备进行细分。 如果网页在所有三个 Core Web Vitals 指标的第 75 百分位处达到建议的目标,则评估 Core Web Vitals 合规性的工具应将其视为通过测试。

以上是官方的介绍,具体来讲就是:

  • LCP 指的是 Largest Contentful Paint,即最大内容绘制时间,衡量的是页面主要内容加载速度,即用户“感觉页面是不是出来了”。
    • 这里说的最大内容,是指渲染在视口中 面积最大的内容元素 ,它完成渲染的时间就是LCP。
  • INP 指的是 Interaction to Next Paint,即下次绘制的交互延迟,衡量的是用户和页面互动时的延迟——比如点击按钮、输入框打字等。
    • 这里说的延迟,是指从用户操作到页面下一次渲染完成的时间间隔,即交互响应速度。
  • CLS 指的是 Cumulative Layout Shift,即累计布局偏移,衡量的是页面在加载过程中布局跳动的程度。
    • 例如:
      • 图片加载晚导致文字突然被挤下去
      • 广告突然插入页面让内容跳动
      • 字体加载切换导致布局变化

那这三个值又是如何计算的呢?

  1. LCP

其计算分为以下几步:

  • 浏览器会监听页面中可能成为 LCP 的元素
    • 包括:img、video 的 poster、有 background-image 的 block 元素、大文本块(基于渲染面积)
  • 每当「一个候选元素被渲染」或「变更大小」时,浏览器重新判断:
    • 当前视口中 面积最大的内容元素 是哪一个?
  • LCP 的值 = 这个最大元素完成渲染的时间点
    • LCP = 该元素完成绘制的时间(相对 navigationStart),包括:内容下载完成、解码完成、实际绘制到屏幕(paint)
  1. INP

INP = 所有交互事件的 最大(或近似最大)延迟

浏览器监控三类事件:

  • click、tap
  • pointer events
  • keyboard input

对于每次事件,浏览器记录三段时间:

  • Input delay(输入延迟):事件触发 → JS 事件监听器开始执行
  • Processing delay(处理延迟):JS 事件处理函数执行时间
  • Presentation delay(呈现延迟):JS 执行完成 → 下一帧内容绘制完成

即:单次交互延迟 = Input Delay + Processing Delay + Presentation Delay

INP 的最终值计算方式:

  • 浏览器不会拿“所有事件的最大值”,避免一次偶发卡顿打爆数据。
  • 而是:使用 99th percentile(99 百分位值) 或最长的那一小部分事件。

最终:INP = 所有交互事件延迟中的 99 百分位

所以它代表你页面中“卡得最严重的一次(或几次)交互”。

  1. CLS

CLS = 所有 layout shift 的得分累加。

一个布局偏移分数的计算如下: Layout Shift Score = Impact Fraction × Distance Fraction

下面解释 2 个参数:

(1)Impact Fraction:影响区域比例

影响区域 = 偏移前区域 ∪ 偏移后区域

比如:

  • 一个 div 往下跳了 100px
  • 它占屏幕高度 20%

影响区域可能是:该 div 原位置 + 变化后的区域

Impact Fraction = 影响区域面积 / 视口面积

(2)Distance Fraction:移动距离比例

即元素移动了屏幕的多少比例:Distance Fraction = 元素移动距离 / 视口高度

(3) CLS 单次偏移得分 = Impact × Distance

然后浏览器将 多个偏移事件的得分不断累加:

CLS = Σ(每一次 layout shift 的得分)

但:

  • 在 1 秒内
  • 且连续的偏移事件
  • 会组成 “session window”

CLS 最终采用的是所有 session window 中最大的一个。

最后补充几点:

  • 对于LCP,浏览器会不断更新,直到用户第一次交互或页面进入 stable 状态
  • 对于每次交互事件,INP都可能发生变更
  • 对于每次布局(位移和尺寸)变化,CLS 会计算一次得分并累加

官方说的第 75 百分位表示什么意思

比如你收集了100个用户的LCP、INP、CLS数据,按照从小到大的顺序排列,第75个值就是第 75 百分位,如果这个值小于等于建议的目标值,那说明当前网站的性能是健康的。

FCP

除了以上 3 个指标,还有一个指标是 FCP(First Contentful Paint),它指的是页面首次绘制任何内容的时间。

页面加载与内容绘制性能指标

为了提供良好的用户体验,网站应尽量将首次有意义的绘制时间控制在 1.8 秒或更短的时间。为确保大多数用户都能达到此目标值,一个合适的衡量阈值是网页加载时间的第 75 个百分位数,并按移动设备和桌面设备进行细分。

基于上面的描述,其实可以简单把 FCP 作为白屏的一种参考指标,但是它并不能完全代表白屏时间,比如有些页面可能会通过 skeleton 来作为过渡,这种状态下,用户其实也会认为是一种白屏状态。

自定义业务指标

虽然业界已经有了较权威的 Core Web Vitals 指标,但是在实际业务场景中,可能并不能反映真实情况。

举一个商品列表的例子,它的加载流程是这样的:

  • 加载页面资源
  • 渲染背景图
  • 加载商品列表数据
  • 渲染商品列表

在这个过程中,我们核心的视图其实是商品列表,而不是背景图。但是,因为背景图非常大,它会直接触发LCP,导致数据上报不准确。

为了解决这个问题,我们需要在商品列表加载完成后,手动触发LCP的上报。

  • 如果我们是 vue 项目,那可以在 mounted 钩子中触发
  • 如果是 react 项目,那可以在 useEffect 中触发(依赖项为空数组)

不同的业务场景,可能需要上报🛫的指标也不同。

性能数据上报与统计

对于业界通用指标,主要涉及到两块:

  • performance API
  • web vitals sdk

性能数据分析

OK,性能如何量化以及如何衡量我们大概知道了,那接下来就需要根据这些指标来分析我们的性能瓶颈。

一般我们可以从两个视角出发:

宏观视角

  • 空间差异:不同的地区、场所下,性能可能存在差异,比如:地下停车场的网络信息不好,导致性能较差。
  • 时间差异:流量高峰期、大促、拉新活动等时期,服务器的抗压性若较弱,性能可能较差。
  • 内容差异:营销页往往会包含很多图片、视频素材,导致拖慢性能。

统计视角:

  • 100分位:一般100分位的数据可能都是极端数据,所以我们一般不采用。
  • 90分位:90分位能够反映大部分用户访问时的性能水平。
  • 75分位:相比于90分位,更加稳健,因为90分位还是可能存在一些极端数据。这也是 web core vitals 的建议值。
  • 50分位:特别稳健,完全不受极端数据影响。但是,不能反映绝大部份的性能情况。
  • 平均值:计算比较简单,容易受极端数据影响,基本无法反映真实性能情况。

所以,在宏观视角的前提下,一般情况下,我们会在 90 分位、75分位间选择。虽然我日常的性能优化工作主要涉及的是小程序,指标也基本上参考小程序官方的指标设计,但我觉得前期可以采用 75 分位,后期再采用90分位,即分为两个阶段:

  • 前期做到性能可接受:75分位
  • 后期做到性能尽量极致:90分位

性能优化的常见抓手

网络连接阶段

  • http2/http3:
    • 基于http2.0的二进制传输、多路复用、头部压缩,一方面提高了解析、传输效率,另一方面解除了并发请求限制,我们不再需要把一些小图片转成base64格式。
    • 当然,http2.0仍然存在队头阻塞,即一个请求流的包丢失了,会阻塞所有请求流。
    • http3.0基于quic协议,该协议底层基于udp,解决了连接级的队头阻塞问题,即一个流的包丢失只会阻塞该流,其他流不受影响。
  • DNS预解析:
    • 使用 <link rel="dns-prefetch" href="//example.com"> 告知浏览器提前进行 DNS 解析,将域名转换为 IP 地址,减少用户点击链接时的 DNS 查询延迟。
  • TCP预链接:
    • 使用 <link rel="preconnect" href="//example.com"> 告知浏览器提前与目标服务器建立连接,包括 DNS 解析、TCP 握手和 TLS 协商(如果是 HTTPS),进一步减少后续请求的延迟。
  • keep-alive:
    • HTTP 持久连接(Persistent Connection),允许在同一个 TCP 连接上发送和接收多个 HTTP 请求/响应,避免了每次请求都重新建立 TCP 连接(三次握手)和 TLS 握手的开销。
  • 减少重定向:
    • 每次重定向都会触发一个新的 HTTP 请求,增加至少一个 RTT(往返时延),严重影响页面加载速度,尤其是在移动网络环境下。应尽量避免不必要的 301/302 重定向。
  • CDN加速:
    • 内容分发网络(Content Delivery Network),将静态资源缓存到离用户最近的边缘节点。用户请求时可直接从最近的节点获取资源,大幅降低网络延迟和丢包率,同时减轻源站压力。

首次请求

  • Gzip/Brotli压缩:
    • 开启 HTTP 压缩可以显著减少 HTML、CSS 和 JavaScript 等文本资源的传输体积。Brotli 是比 Gzip 更新的算法,通常能提供更高的压缩率(约 20%),大多数现代浏览器都已支持。
  • Early Hints 103:
    • 一个 HTTP 状态码,允许服务器在生成最终 HTML 响应(200 OK)之前,先发送一个包含 Link 头部的 103 Early Hints 响应。这让浏览器可以在服务器还在处理主请求时,就开始预加载关键资源(如 CSS、JS),利用了服务器思考的时间(Server Think Time)。
  • 流式SSR(Streaming SSR):
    • 传统的 SSR 需要等待整个 HTML 页面在服务端生成完毕后才一次性发送给客户端。流式 SSR 允许服务端生成一部分 HTML 就立即发送一部分,浏览器可以边下载边解析渲染。这能显著降低 TTFB(Time To First Byte)和 FP(First Paint),让用户更快看到页面内容。

资源加载

  • 代码体积优化:
    • TreeShaking:移除 JavaScript 上下文中未引用的代码(Dead Code)。
    • 代码分割(Code Splitting):将代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。
  • 代码压缩混淆:
    • 移除代码中的空格、注释、换行符,并缩短变量名,从而减小文件体积。混淆还能增加代码被逆向工程的难度。
  • 图片体积优化:
    • 格式选择:优先使用 WebP 或 AVIF 等现代图片格式,它们在相同质量下通常比 JPEG/PNG 体积更小。
    • 压缩:使用工具(如 TinyPNG, ImageOptim)去除图片元数据并进行有损/无损压缩。
    • 响应式图片:使用 srcset 和 sizes 属性,根据设备屏幕大小加载合适尺寸的图片。
  • 字体裁剪(Font Subsetting):
    • 只保留页面实际使用到的字符(如仅保留中文常用字或特定标题字符),极大地减小字体文件体积(中文字体文件通常很大)。
  • HTTP缓存:
    • 强缓存(Cache-Control/Expires):浏览器直接从本地缓存读取资源,不向服务器发送请求(状态码 200 from disk/memory cache)。
    • 协商缓存(Last-Modified/ETag):浏览器发送请求询问服务器资源是否更新,如果未更新,服务器返回 304 Not Modified,浏览器继续使用本地缓存。合理搭配使用可大幅减少网络流量。
  • 资源、路由、组件,预加载/懒加载:
    • 资源预加载(Preload/Prefetch):
      • preload:<link rel="preload"> 告诉浏览器立即加载当前页面必需的关键资源(如字体、首屏大图),优先级高。
      • prefetch:<link rel="prefetch"> 利用浏览器空闲时间下载将来(如下一个页面)可能用到的资源,优先级低。
    • 路由懒加载:
      • 利用打包工具(Webpack/Vite)的代码分割功能,将不同路由对应的页面组件拆分成独立的代码块(Chunk),仅在用户访问该路由时才加载对应的 JS/CSS 文件,是单页应用(SPA)优化的标配。
    • 组件懒加载:
      • 特别适用于 弹窗(Modal)、抽屉(Drawer)、复杂的下拉菜单 等非首屏立即展示的交互组件。
      • 原理:通过动态 import() 语法将这些组件的代码从主包中分离。只有在用户触发特定操作(如点击打开按钮)时,才发起网络请求加载代码。这能显著减小首屏 JS 体积,加快首屏 TTI(Time to Interactive)。
  • Service Worker、离线包、静态资源预热:
    • Service Worker:
      • 核心原理:它像一个驻留在浏览器后台的“中介”或“代理服务器”,独立于当前页面运行。
      • 实战配置与性能优化(推荐使用 Workbox):
        1. 预缓存(Precaching):在构建时自动将核心资源(HTML Shell、关键 JS/CSS)加入预缓存列表。应用启动时直接从本地 Cache Storage 读取,实现秒开和离线访问。
        2. 运行时缓存策略(Runtime Caching):
          • CacheFirst(缓存优先):适用于图片、字体等不常变的静态资源。一旦缓存,后续请求直接走本地,完全不消耗网络。
          • StaleWhileRevalidate(看旧更变新):适用于头像、新闻列表等。优先返回本地旧数据让页面快速显示(不留白),后台同时发起网络请求更新缓存,下次访问即为最新。
          • NetworkFirst(网络优先):适用于 HTML 主文档或关键 API。优先尝试获取最新数据,网络失败时才降级使用本地缓存兜底。
    • 离线包(Offline Package):
      • 常见于 Hybrid App(混合应用)。将 HTML/CSS/JS/图片等静态资源打包并内置在 App 客户端中。当用户打开 WebView 时,直接从本地文件系统加载资源,无需通过网络下载,实现秒开体验。
    • 静态资源预热(Resource Hinting / Warm-up):
      • 这里的“预热”可以指在 WebView 初始化之前提前启动浏览器内核,或者在用户点击链接前提前建立连接(Preconnect)和下载资源(Prefetch)。在服务端层面,CDN 节点也可以进行缓存预热,将源站资源提前推送到边缘节点,避免用户首次请求回源。

页面解析

  • 减少 DOM 深度:
    • DOM 树过深会增加内存占用,导致样式计算(Recalculate Style)和布局(Layout/Reflow)变慢。应尽量扁平化 HTML 结构,使用 Flexbox/Grid 布局代替多层 div 嵌套。
  • 关键 CSS 内联(Critical CSS):
    • 将首屏渲染必须的 CSS 直接写在 <style> 标签内放在 <head> 中。这样浏览器下载完 HTML 就能立即渲染首屏,无需等待外部 CSS 文件下载,消除渲染阻塞(Render Blocking)。
  • script异步加载:
    • async:脚本下载完立即执行,执行顺序不确定,适合独立的统计脚本。
    • defer:脚本并行下载,但会等到 HTML 解析完成后,按在 HTML 中出现的顺序执行。推荐用于业务逻辑代码,因为它不会阻塞 HTML 解析器构建 DOM 树。
  • 减少解析阻塞:
    • JS 和 CSS 都会阻塞渲染。除了使用 async/defer(仅仅针对script),还应将非关键 CSS(如打印样式)通过 media 或者 preload 进行控制。
  • 避免强制同步布局(Forced Synchronous Layout):
    • 浏览器通常会批量处理 DOM 修改以优化性能。但如果你在修改 DOM 后立即读取布局属性(如 offsetHeight),浏览器为了返回准确值,不得不立即触发布局计算(Reflow)。
    • 优化:读写分离,先批量读取所有需要的数据,再批量写入 DOM。
  • CSS选择器优化:
    • 浏览器匹配 CSS 选择器是从右向左进行的。虽然理论上减少层级和避免通配符能提升性能,但在现代浏览器引擎优化下,差异已微乎其微。
    • 建议:关注代码的可维护性(如使用 BEM 规范、Tailwind CSS)远比过度纠结选择器性能重要。避免使用过于复杂的 :nth-child 或深层嵌套即可。

首屏渲染

  • SSR (Server-Side Rendering) / SSG (Static Site Generation):
    • SSR:服务器实时生成 HTML 返回给客户端,首屏内容立即可见,SEO 友好。适合动态内容。
    • SSG:构建时生成静态 HTML,部署在 CDN 上。访问速度最快,但只适合内容不常变动的页面(如博客、文档)。
  • 模板化创建:
    • 在客户端渲染(CSR)中,避免使用 JS 逐个创建 DOM 节点。直接使用 HTML 模板字符串或 <template> 标签批量插入,减少 DOM 操作次数。
  • 关键渲染路径(Critical Rendering Path)优化:
    • 缩短从 HTML 接收到像素绘制到屏幕的过程。核心是消除阻塞资源(CSS/JS),让浏览器尽快构建出 Render Tree。
  • 骨架屏(Skeleton Screen):
    • 在数据加载完成前,先展示页面的大致结构(灰色占位块)。虽然不能加快实际加载速度,但能显著降低用户的心理等待时间,提供更好的感官体验。
  • 渐进式渲染(Progressive Rendering):
    • 分优先级:优先渲染视口内(Above the Fold)的核心内容,视口外的模块延迟渲染。
    • 分层:先渲染低清占位图,加载完后再替换为高清图。
  • 优化 LCP 元素(Largest Contentful Paint):
    • 确保页面中最大的内容元素(通常是大图或 H1 标题)尽早渲染。
    • 手段:对 LCP 图片使用 <link rel="preload"> 预加载,并确保该图片没有被懒加载(Lazy Load)。

异步数据请求

  • 接口合并/拆分:
    • 合并:在 HTTP/1.1 时代,为了减少 TCP 连接建立的开销,通常将多个小接口合并为一个大接口(BFF 层常做此事)。
    • 拆分:在 HTTP/2 多路复用环境下,或者为了避免首屏被慢查询阻塞,可以将一个聚合大接口拆分为核心数据接口(快)和非核心数据接口(慢),让核心内容先展示。
  • 请求并发控制:
    • 对于http1.1,浏览器对同一域名的并发请求数有限制(通常是 6 个)。如果有大量请求同时发起,会导致后续请求排队等待(Stalled)。可以通过实现一个并发调度器(Scheduler)来控制同时发起的请求数量。
  • 接口缓存策略:
    • 可以在 HTTP 头中设置 Cache-Control,或者在前端代码层(如 Axios 拦截器、React Query)实现内存缓存。对于一段时间内不变的字典数据、配置数据,直接使用缓存。
  • 数据预拉取(Prefetching):
    • 在用户进行某些操作(如鼠标悬停在菜单上)时,预测用户即将访问的内容,提前发起 API 请求。像我们之前对小程序的优化中,我们在点击跳转到另一个页面时,会提前请求接口,将数据保存到global中,当进入下一个页面,且global中已经拿到了数据,则直接使用预请求的数据。
  • 请求去重:
    • 避免在短时间内对同一个 URL 发起多次重复请求(例如用户快速点击按钮)。可以通过loading状态、防抖、节流等方式来避免。当然也可以通过请求锁的形式来实现,即每次请求前先判断是否有正在进行的请求,如果有,则等待,等正在进行的请求完成后,再用之前的结果,这种一般用于数据不经常变化的场景。
  • 离线数据缓存:
    • 将 API 数据存入 IndexedDB 或 localStorage。当网络断开或弱网时,优先展示本地存储的旧数据(Stale Data),提示用户当前处于离线模式。
  • SSE (Server-Sent Events) / WebSocket:
    • 对于实时性要求高的数据(如股票、聊天),传统的轮询(Polling)效率极低。
    • SSE:基于 HTTP 的单向通信(服务端推送到客户端),轻量级,适合消息通知。
    • WebSocket:基于 TCP 的全双工通信,适合高频交互场景。

数据渲染

  • 虚拟滚动(Virtual Scrolling):
    • 当需要展示成千上万条数据(如长列表)时,只渲染用户视口(Viewport)可见范围内的 DOM 元素。随着滚动动态复用或替换这些节点,保持 DOM 数量恒定,避免浏览器因 DOM 过多而卡顿。
  • 分页/无限滚动:
    • 分页:传统 Web 常见的方案,明确告知用户数据总量,按需跳转。
    • 无限滚动:移动端常见方案,滚动到底部自动加载下一页。需要注意内存控制,避免加载过多导致页面崩溃(通常需配合虚拟滚动)。
  • 数据分批渲染(Time Slicing):
    • 如果一次性要渲染大量数据(且无法使用虚拟滚动),可以将数据拆分成多个小批次(Chunk),利用 requestAnimationFrame 或 requestIdleCallback 分帧渲染。这样可以避免主线程长时间阻塞,保证页面还能响应用户交互。
  • 增量更新:
    • 仅更新发生变化的数据部分,而不是重新渲染整个列表或组件。现代前端框架(React/Vue)的 Diff 算法就是为了实现这一点,但在业务逻辑层面也应注意只传递变更的数据。
  • 组件缓存:
    • React:使用 React.memo 避免父组件更新导致无关子组件的重复渲染;使用 useMemo/useCallback 缓存计算结果和函数引用。
    • Vue:使用 <keep-alive> 包裹组件,在组件切换(如 Tab 切换)时将状态保留在内存中,避免重复销毁和创建。
  • 避免不必要的渲染:比如仅仅变更了 state A ,不要触发 state B 的重新渲染。

交互就绪

  • 代码分割(Code Splitting):
    • 将代码拆分为多个小块,按需加载。比如基于路由的分割,或者将第三方库(Vendor)单独打包。这样用户访问特定页面时,只需下载该页面所需的代码,减少首屏包体积。
  • 懒加载(Lazy Loading):
    • 对于非首屏可见的组件或模块,使用动态导入(import())进行懒加载。等到用户交互(如点击按钮、滚动到特定区域)时再请求资源。
  • 减少 JS 执行时间:
    • 移除未使用的代码(Tree Shaking)。
    • 优化算法复杂度。
    • 避免在主线程进行密集计算,防止阻塞渲染和交互。
  • Web Worker:
    • 将繁重的计算任务(如大文件解析、图像处理、复杂数据排序)移至后台线程运行,避免阻塞主 UI 线程,保持页面流畅响应。
  • 长任务拆分(Long Task Splitting):
    • 任何超过 50ms 的任务都会被视为长任务,可能导致页面卡顿。将长任务拆解为多个微任务或宏任务(使用 setTimeout、requestIdleCallback 或 scheduler.postTask),在任务间隙把控制权交还给浏览器,以便响应用户输入。
  • 防抖(Debounce)与节流(Throttle):
    • 防抖:高频触发事件(如搜索框输入)停止触发 N 毫秒后才执行函数。
    • 节流:高频触发事件(如滚动、窗口缩放)在指定时间间隔内只执行一次。
    • 两者都能有效减少昂贵的回调函数(如 DOM 操作、API 请求)的执行频率。

持续优化

  • 意识增强
  • 性能监控:数据采集、看板、报警
  • 监督机制
  • 经验再转化

性能优化常用策略

通常,我们可以基于一个原则:根据长期/短期目标,选择ROI合适的方案并渐进式优化

短期ROI

普遍成本低,见效快,业务层几乎无改动,可通过配置/工程化实现

  • http2/3、DNS预解析等
  • Gzip/Brotli 压缩
  • http 缓存策略
  • tree shaking
  • 内联关键css
  • 代码压缩混淆

中期ROI

成本相对高一些,业务层代码需要修改

  • 图片、字体优化
  • 代码分割,懒加载
  • 首屏依赖治理
  • 业务逻辑优化
  • 骨架屏
  • 离线数据缓存

长期ROI

成本高,业务层代码可能需要重构才能实现

  • 渐进式渲染
  • SSR/SSG
  • 数据预拉取
  • Web Worker
  • Early Hints

如何保持长期的性能稳定

其实就是抓手中的持续优化内容。

意识增强

这块可以通过设定责任人制度来强化,比如不同的模块分配给不同的人,并由一个主负责人来负责协调。

性能观察

性能观察可以分为三个部分:

  • 数据上报收集
  • 监控看板与报警通知
  • 针对一些较明显的数据特征直接映射成某些case(随着沉淀的越多,映射的case也会越多)

监督机制

  • 定期检查并同步
  • 优化经验不断沉淀,并不断优化治理体系

经验再转化

  • 通用共性优化方案
    • 工具化:
      • 构建期:与前端工程基建结合
      • 运行时:收敛为运行时SDK
    • 流程化:SOP + 检查校验
  • 业务领域逻辑优化
    • 业务模板沉淀
    • 业务领域SDK沉淀