C端购物类应用的性能优化实践(一)

如何收集和评估首屏性能优化的效果

针对用户访问的关键路径,比如从首页跳转到列表页、从首页跳转到个人中心、从列表页跳转到商详页等,哪些关键页面需要做首屏优化,确定下来范围。

制定每个页面场景的首屏优化目标,比如打开首页,现阶段在低端机上花了2000ms,只要首屏优化到1000ms内就算达标(低端机容易出现性能问题,高端机在性能问题上的表现不明显)。

测试人员根据关键路径,使用低端机操作页面转场,并进行视频录制,将1秒划分为60帧,即每帧约16.7毫秒(为了方便计算,可以改为每一帧20毫秒)。由于网络等影响因素比较多,每一次录制得出的数据都不一样,测试人员需要重复录制10次,去掉10次中的异常数值后取平均值,该平均值作为本次统计的耗时。

举个例子,视频中在第915帧点击了列表页商品,开始转场跳商详,在第970帧时商详页展示主UI,那么耗时为(970-915)*20=1100ms

另外,测试人员需要在发版前、发版后都进行录制,要求发版前录制主要是为了避免性能问题暴露到生产环境上。

排查工具使用

git

有时候发版前,测试人员跟你说xx页面的性能数据比前一天的上涨了几百毫秒,你可以通过指定目录下的24小时内git commit内容改动,快速定位有性能问题的代码改动。

Performance

可以通过谷歌浏览器的Performance面板录制具体页面的trace,进行分析,记得把cpu降速6x

Memory

可以用来排查内存泄漏,先点击强制进行垃圾回收,然后观察指标是否大幅上升

资源的优化

打包工具的资源分析插件

可以用上述插件进行包体积的分析,做针对性的优化,比如js chunk拆分等。举个常见案例,常量的代码如果和工具方法的代码都写在一个js文件里,常量在多个页面被使用,而工具方法只在一个页面被使用,打包时常量和工具方法被打进一个chunk里,其他页面引用该chunk有多余资源浪费。

资源的预取

<link rel="preload" href="https://xx.com/a.webp" as="image"> preload表示资源的优先级最高,比如可以把首屏加载的图片设置为preload。

<link rel="prefetch" href="https://yy.com/915443.js" as="script"> prefetch表示该资源在当前页面用不到,但是下个页面可能会用到,会等当前其他资源加载好了之后,下载队列空闲,该资源才会被下载缓存起来。比如首页跳转列表页,在首页设置prefetch列表页的主chunk。github.com/GoogleChrom... ,quicklink这个库是跟prefetch有关的。

<link rel="preconnect" href="https://cc.com"> preconnect的作用是提前和目标服务器进行连接,比如提前和图片服务器连接好。

<link rel="dns-prefetch" href="https://dd.com"> dns-prefetch的作用类似preconnect

script async: 解析html时,如果遇到标识为async的script标签,不会阻塞解析HTML的剩余代码,会异步请求加载该js脚本,但是脚本加载完成之后会阻塞停止解析html,立即执行脚本,最后等执行完脚本再继续解析html代码。

script defer: 解析html时,如果遇到标识为defer的script标签,不会阻塞解析HTML的剩余代码,会异步请求加载该js脚本,但是脚本加载完成之后不会立即执行脚本,而是等解析完html代码再执行该js脚本。

图片的优化

图片自动转webp

在首页、列表页等ssr页面,判断设备是否支持webp,将结果写入cookie中,这样做可以在node端读取cookie判断,支持webp的话就进行转换(需要图片服务器同时提供png、webp等格式)

js 复制代码
const isSupportWebp = function () {
  if (typeof window === 'undefined') {
    return false
  }
  const $canvas = document.createElement('canvas')
  const data = $canvas && $canvas.toDataURL('image/webp', 0.5)
  return data.indexOf('data:image/webp') === 0
}

const transformWebp = function (imgUrl) {
  const imgHosts = ['img1.com', 'img2.com']
  const canTransform = imgHosts.some(host => imgUrl.includes(host))
  if (canTransform) {
    return imgUrl.replace(/\.(jpg|jpeg|png)$/, '.webp')
  }
  return imgUrl
}

图片手动预取

比如我们可以在首页空闲时主动预取一些个人中心的图片,当跳到个人中心时图片就是从缓存中读取

js 复制代码
export const preloadImgs = async (imgs = [], timeout = 3000) => {
  return Promise.race([
    new Promise((resolve) => {
      let loadedNum = 0
      const imgLoadHandle = () => {
        loadedNum++
        if(loadedNum === imgs.length) {
          resolve()
        }
      }
      imgs.forEach((src) => {
        const img = new Image()
        img.src = src
        img.onload = imgLoadHandle
        img.onerror = imgLoadHandle
      })
    }),
    new Promise((resolve) => {
      setTimeout(() => {
        resolve()
      }, timeout)
    })
  ])
}

在测试图片的预取是否生效时记得不要开启浏览器Network面板的disable cache,开启了会导致图片缓存不生效。

lazyload

非首屏图片设置lazyload

图片裁切

手机宽度有很多种宽度,ipad设备宽度一般比手机宽度大很多,根据设备宽度获取不同size的图片(图片服务器需要提供不同size的图片),比如375宽度只需要750宽度的图片,不需要获取1366宽度图片,750宽度图片比1366宽度图片小很多。

更多内容看第二篇(第二篇还没写完)

相关推荐
腾讯TNTWeb前端团队5 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰8 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪8 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪8 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy9 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom10 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom10 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom10 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom10 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom10 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试