跳到主要内容

谈谈性能优化

分析原因, 通过chrome的性能分析工具 Lighthourse、浏览器的performance的性能工具

对于性能优化,从整体项目来看,结合用户体验网页性能和代码层面优化、打包构建优化等,其实一般首先通过监控埋点来进行分析针对性优化

  • 通过浏览器的性能监控指标来分析 :TTFB、FP、FCP、LCP、FID、CLS、TBT

    • 通过这些指标分析 页面加载速度、交互性、总阻塞时间、接口响应时间等

    TTFB、FP (首次绘制)、FCP(首次内容绘制)、LCP(最大内容绘制)、FID(首次输入延迟)、CLS(累计布局偏移)、TBT(总阻塞时间)

  • 【window.onerror、window.addEventListener('error', ...)】,通过监控 JavaScript 错误、网络请求失败等异常情况

性能指标分析​

  • 页面测速工具:https://pagespeed.web.dev/
    • Google 提供的网页性能分析工具
    • PageSpeed 展示真实用户数据情况,单独服务器上运行,对比lighthouse 结果通常会更准确、更一致
  • 深度性能分析测试:https://www.webpagetest.org/
    • 性能抓包 、诊断工具,可以看到非常细的加载过程
    • 用来分析一个网页在不同地区、设备、浏览器、网络条件下的真实加载表现。
  • lighthouse
  • Performance

首屏加载性能优化​

  • 减少http请求
  • 按需加载/异步加载
    • 组件、路由按需加载
  • 体积优化
  • 字体文件加载优化
  • requestAnimationFrame分步渲染
  • defer和async应用
    • defer延迟加载 : 加载完后等dom解析完之后再执行。
    • async异步加载脚本, 浏览器空闲再执行
  • preFetch、preLoad应用
  • gzip压缩
  • 静态资料配合OSS+CDN加速
  • 网络请求速度优化
  • 图片懒加载

减少http请求​

  • 1、利用浏览器缓存
    • 强缓存、协商缓存
  • 2、合并JS和CSS文件
    • 通过webpack/vite构建工具
  • 3、图片可以滚动加载、雪碧图

开启gzip​

开启gzip之后,效果是非常明显的,压缩后一般能减少响应文件的50%-70%的大小

  • 压缩 Gzip,服务端要花时间;解压 Gzip,浏览器要花时间
  • 服务器上可以开启gzip, 在客户端中通过构建工具(webpack/vite)也可以进行gzip压缩
    • 通过客户端生产gzip的话,可以分担服务端的压力
      • 大量的文件需要服务端压缩的话,也是非常消耗服务端的性能
    • 如果客户端生成,服务端先判断是否存在的后缀名为gz的文件,直接去拿,不存在就来压缩
    • 虽然在客户端开启gzip,打包生产的时候,会慢一些,但是我们发布打包生产只需要打一次包,而服务器是面对多个用户访问的 (每个用户访问的话,对服务器压力是很大的)

注意:如果代码体积不是很大的话,可以不开启gzip,因为开启gzip,浏览器解压也需要时间

gzip原理​

当请求静态资源时,服务端发现请求资源为gzip的格式时,设置响应头 content-encoding: gzip

  • gzip实际上把js文件进行了类似zip压缩包的
  • 前端上传js到CDN静态资源服务器上,上传的大小比如是1M, 当浏览器访问的时候,读取的实际上是被压缩过的类似zip压缩的文件,就叫gzip, 如300k

打包优化​

  • 组件库打包优化
    • antd css打包优化
      • 在antd4版本中,antd的js会做分包,但是css没有,需要引入import 'antd/dist/antd.css'

        • 这样会导致css文件打包出来很大
        • 可以利用antd提供的babel插件去优化
      • antd5中,有升级方案,从less => css in js

        • 核心就是在js中写css,然后把样式通过style标签插入页面中

react性能优化​

渲染优化​

  • 减少render次数

    监控render次数的原则: 复杂的组件、顶层的组件才需要

    • 简单的组件不需要监控render,没什么必要,简单的组件性能可以忽略不计
    • 因为监控组件render次数也是需要成本,如果页面组件太多,需要花时间成本去排查
    • 把时间花在刀刃上,主要监控复杂组件和的顶层组件就可以了
    • 复杂组件:表格、表单、图表、复杂的配置项等
    • 注意顶层组件的render次数
      • 如果顶层组件都render多次,那么它下面的子组件也会多次render
    • 注意state状态改变,就会触发组件重新渲染,要留意state状态的值触发改变的逻辑
    • 对于class类组件,使用shouldComponentUpdate/PureComponent
      • 根据 shouldComponentUpdate 的返回值,决定是否对组件进行重新渲染
      • PureComponent,会对props 和 state 进行浅比较, 对于基本数据类型会判断值是否相等,而对于引用类型数据判断引用是否相等,不相等则重新渲染
    • 函数式组件,使用React.memo
      • React.memo只会浅比较props的状态,发生变化则会重新渲染
      • 可以缓存函数组件的渲染结果
    • 注意PureComponent和React.memo浅比较的问题,当数据是嵌套多层的对象或数组时,当修改了本身的对象其中的属性,但是引用类型地址还是一样的,从而导致没有触发重新渲染
      • 应该使用不可变的数据结构,不要直接去修改数据的状态,应该返回一个新的数据状态
      • 社区上比较出名有immer这个库,专门解决这个问题,可以确保每次状态更新都会生成一个新的对象,这样浅比较就可以检测到变化,触发组件渲染
        • immer的原理,是基于proxy来实现的,会创建一个状态state的副本,当状态被修改的时候,会被proxy拦截监听到会记录下来 ,然后会返回一个新的对象
        • immer的性能是比较好的,只复制发生变更部分,而未修改的部分是不会变化的, 未修改的部分还是保持同一个引用,这样减少了内存的消耗,提高这个性能,只有发生修改的部分才会创建新的副本
    • 使用why-did-you-render库来检测组件是否重复渲染
  • useMemo

    • 对于复杂的数据,可以进行数据缓存、优化渲染
  • useCallback

    • 对于复杂的逻辑,可以进行缓存函数
  • keep-alive

    • 组件切换过程中保留状态在内存中,可以防止重复渲染DOM
  • useTransition开启并发渲染,降低任务优先级

    • useTransition是降低渲染优先级,可以标记代码渲染优先级不高,可以优先处理其它优先级更高的渲染任务
    • useTransition 本质上是为了解决了渲染并发的、不阻塞ui更新的问题
    • 其实就是通过Fiber架构,将一个大的渲染任务分为很多小任务,按照优先级来执行这些任务分片,优先级高的会先执行

    当一次render要渲染数据量大的时候,可以使用useTransition来进行优化,让页面可以响应用户的交互,但是此方法仅仅优化于reconciler调度的render阶段,没法优化commit提交阶段

加载优化​

  • 利用React.lazy 和 React.Suspense组件按需加载

    • 利用 React.lazy 和 React.Suspense 延迟加载不是立即需要的组件。

    • 当前不需要的组件不应该加载,我们应该尽量做到按需加载

vue性能优化​

vue内部已经做了很多性能优化,比如vue通过数据响应式追踪可以做到精确的组件更新, 以及template模板优化 【静态分析、静态提升】

  • keep-alive

  • v-if与v-show

  • computed

  • v-memo

  • 在vue2中合理使用Object.freeze

    • 对于不需要响应式的数据,合理使用Object.freeze,可提高性能
    • 比如有巨大的数组或Object,并且确信数据不会修改,使用Object.freeze()可以让性能大幅提高
  • 在vue3中合理使用toRow/markRaw

    • toRow场景

      • 比如临时使用的变量,不需要进行响应式的监听,修改这个对象,不需要页面更新,避免这个性能开销,可以使用toRow 来将响应式对象转换为普通对象
    • markRaw场景

      • 有些值不应被设置为响应式的,如第三方库,组件等
      • 当渲染具有不可变数据源的大列表时,可以不使用响应性代理,从而提高性能

    toRow将响应式数据转换为普通数据

    markRaw标记一个对象,永远都不成为这个响应式对象

小程序性能优化​

  • 小程序首屏加载优化
  • 小程序包体积优化
  • setData优化
    • setData不能频繁调用, 每次setData都要经过逻辑层和渲染层,逻辑层和渲染层是不同进程。频繁调用每次经过2个进程会影响性能和用户体验
  • 图片优化
  • 内存优化
  • 使用worker
  • 主包优化

大数据渲染优化​

  • 时间切片
    • 长任务分割为一个个执行时间很短的任务
  • 利用requestAnimationFrame
    • 可以使用requestAnimationFrame来分步渲染
    • requestAnimationFrame是浏览器在下一次重绘前执行
  • 使用webworker线程
    • 将大数据的计算放在worker中,不影响主线程
  • 虚拟滚动列表

css优化​

  • 减少回流与重绘

    • transform 性能高,可以来替代position来布局元素位置
  • 使用transform 不会引起回流与重绘的

  • 减少无用的css

  • UI 库全量引入问题优化

  • 优先砍掉首屏没用到的样式

  • css缓存复用

  • 压缩css文件

CDN+OSS​

  • OSS 是做存储的,CDN 是做内容加速的
  • 一般二者是配合使用的,对象存储oss里面存的就是一些图片、视频、文件等等,都是静态数据,可以给用CDN做加速
  • 通过cdn对内容进行分发,分发对每个地区中,提高用户访问资源的响应速度
  • 如果数据已经过期,CDN可以发出回源请求,来拉取最新的数据
    • CDN 设置了缓存过期时间,在数据过期后会自动触发回源更新

只使用cdn,不使用oss可以吗

  • 可以只使用cdn,可以直接把静态资源上传到cdn
  • 可以通过cdn做内容分发和存储

工具分析​

web-vitals和Lighthourse​

chrome插件:

  • Web Vitals

    • web-vitals是谷歌针对网页加载速度和体验所提出的一套指标, 如FP、FCP、LCP、FID、CLS、TBT、TTFB

    • 通过此插件可以看到性能指标数据

  • Lighthourse

    • chrome的性能分析工具,灯塔性能分析
    • 通过Chrome浏览器的开发者工具,可以使用Lighthouse进行性能分析
    • Lighthouse会生成详细的报告,包括页面加载性能、性能指标、以及会给出修改的建议

浏览器的performance​

  • 浏览器的performance是浏览器自带的性能工具
  • 可以查看火焰图、帧率、主线程JS执行时间、渲染时间等
    • 火焰图:用于展示页面加载过程中每个任务的耗时情况
    • 帧率:指的是浏览器每秒渲染的帧数

网络层面优化​

静态资源、体积大小、缓存

  • 开启http2

    • 多路复用可以同时发送多个请求和响应,可以并行的传输,解决队头阻塞的问题
    • 头部压缩=>减少了数据传输的大小、二进制协议=>解析起来更高效
    • 提高传输效率
  • 开启gzip压缩

    • nginx开启gzip、前端构建工具gzip
  • 利用缓存,配合强缓存/协商缓存

  • DNS预解析优化

    • link 标签的rel属性设置 dns-prefetch,提前解析域名对应的IP地址
    • DNS解析 会进行解析ip地址 (解析过程中会进行查找,首先通过本地缓存 => 系统 => 本地映射 => 远程DNS源上查找),因此解析DNS过程中需要一定时间,所以就出现了DNS预解析,可以提前解析好ip地址
  • CDN优化

    • CDN缓存,设置合适的过期时间,减少不必要的请求。
      • CDN可以通过配置,定期获取源服务器上的内容,来定期同步更新
      • 如果数据已经过期,CDN可以发出回源请求,来拉取最新的数据
      • 注意如果CDN还没过期,而又重新更新了内容,需要刷新CDN
        • 刷新CDN
          • 可以手动在阿里云上刷新CDN
          • 可以通过脚本,在cicd时,当构建成功时,执行刷新CDN脚本
  • http优化

    • 减少http请求
      • 利用缓存, 通过强缓存从本地缓存中获取资源
      • 图片可以利用图片精灵雪碧图
      • 接口的请求可以考虑合并 (这个需要和后端沟通)
      • 小图片可以转换base64
    • 减少http响应时间
      • 针对于大数据接口请求,响应时间是比较慢,可以考虑分步去请求接口,减少响应时间

图片加载优化​

加载的图片太多或者太大导致页面加载完成慢的问题;图片太多导致向服务器请求的次数太多,图片太大导致每次请求的时间过长,导致用户长时间等待

  • 将图片放在CDN服务器上,加快图片资源的访问速度
  • 配置图片缓存策略,利用缓存图片,直接缓存在浏览器本地,减少重复请求
  • 图片懒加载
    • 页面渲染过程中,图片不会一次性全部加载,会在需要的时候加载,滚动的时候加载
    • 优化这个用户体验,一般会加上占位图
    • 只有当资源即将进入用户视口(可视区域)时才真正加载,从而减少首屏加载时间、降低带宽消耗、提升整体用户体验

实现方案:

  • 1、默认给图片src一个占位图
  • 2、当图片进入可视区域,才把对应的图片链接赋值给src,才会进行加载图片

所有图片一次性加载,导致页面渲染慢原因分析:

  • 浏览器对同一域名的并发请求有限制(通常为6-8个),大量请求会排队等待,导致后续资源(CSS、JS、字体等)也被延迟

  • 首屏一次性发起所有图片的 HTTP 请求,会占用大量网络带宽。

  • 会影响LCP指标(首屏最大内容绘制时间)

懒加载的优势​

  1. 提升首屏速度:只加载视口内及附近的内容,用户打开页面时几乎立刻看到内容框架。
  2. 节省带宽:未滚动的图片完全不请求,特别对移动用户友好。
  3. 减少服务器压力:请求分散在用户滚动过程中,而不是集中爆发。
  4. 改善核心指标:LCP(Largest Contentful Paint)、FID、CLS 等 Web Vitals 指标都会受益

性能监控​