电商网站性能优化 - Part Ⅱ

『23年09月23日』

之前主要在缓存方面对 Website 进行了优化,优化 1。主要解决了第二次及以上访问 app 的速度问题,但是对于用户第一次访问还是会慢一些,整个站点的 Web Vitals 也不是很好。

这是优化前的得分:

Web Vitals Before

所以,决定通过一些分析工具对整个站点做一个全面的性能分析,再对症下药。

那么,有哪些好用的性能分析工具?

  1. PageSpeed Insights
    这是 Google 的在线性能分析工具,可以提供你站点近 28 天的性能数据,包括 core web vitals(FCP,CLS,LCP 等)。
  2. Chrome lighthouse
    谷歌浏览器本地自带的性能分析工具,可以在本地对你的应用进行“跑分”。

以上工具跑完都会得出上图的一份性能报告,报告的内容可以叫做Web vitals,这是 Google 制定的用来衡量 web page 性能的 guideline 。

比如,guideline 中给出了 LCP 应该在站点开始加载后 2.5s 内完成,才对用户比较友好。

Alt text

另外,我们还用到了一个工具 webpack-bundle-analyzer,用来分析 bundle 的大小以及组成。

next.config.js中,修改 webpack 的配置,添加这个插件,插件会在执行yarn analyze时执行。

if (process.env.ANALYZE === "true") {
  try {
    const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
    config.plugins.push(
      new BundleAnalyzerPlugin({
        analyzerMode: "static",
        reportFilename: options.isServer ? "../analyze/server.html" : "./analyze/client.html",
      })
    );
  } catch (e) {
    console.log("bundling error", e);
  }
}

开始行动

打开 PageSpeed Inside 跑一遍站点,等到一个优化前的性能报告。如下:
Alt text

总得分只有 7,其它指标也基本是飘红的。

其中CLS分数过大主要是因为页面有元素发生了偏移,主要是两个注册按钮需要再判断用户有没登陆后才显示,然后又没有为其预留空间,就会导致按钮出现的时候整个页面会整体往下移一下,页面发生了位移,这个比较好解决,只要给两个注册按钮预留空间就可以了。

再看下效果:

CLS

其次,FCP 与 LCP 的分数分别达到了3.0s6.0s,与 Good level 的1.8s2.5s还是有一定的差距。

FCP-Standard

可以仔细揣摩下这两个指标的具体含义和计算方式,FCP(First-Contentful-Paint),简单的来讲就是用户从打开你的站点到看到网站的内容,总共等待了多长时间,如果是 1.8s 内则认为是一个好的体验,不一定要看到全部内容,部分文本,图像,甚至背景图即可。

FCP 计算规则

大家看到可以作弊的地方了嘛 👀,是不是可以搞个小小的隐晦点的 svg?站点加载完再删掉?当然这样没有意义。

LCP Largest-Contentful-Paint,简单来讲就是页面中最大的那个元素的渲染时间,可以是 text block 或 image block。

CLP 计算规则

Example
LCP

对于我们站点来说,LCP 就是首页的 banner 图的渲染时间。

性能报告也有指出哪些地方是可以做优化的

Opportunity模块
Opportunity

  • “Reduce initial server response time” 影响到 FCP、LCP 指标
  • “Reduce unused JavaScript”会影响 LCP 指标

我们的应用是直接 hot start 跑在 AWS 的 EC2 上,没有使用到 CDN,另外首页的内容用 Builder.io进行管理,需要去 builder.io 拉取资讯来做 SSR。整体下来效果不是很好。
还是需要 CDN 加持。

做了个测试:

使用 CDN 之前:

使用 CDN 之后:

可以看到,使用 CDN 资源的响应时间快了五倍多。所以,后续是有计划给站点的静态资源加上 CDN。

对于 Reduce unused JavaScript,简单的理解就是减少页面所有 JS 资源的 bundle 体积。这样可以较少传输、解析与执行的时间。Next.js 的每个页面都会依赖一些公共的 thunk。
比如:
Tree-Shaking-Before

这是 Next.js 打包后的产物,其中First Load JS shared by All指每个页面都需要依赖这些资源,都要去加载这些资源。而我们这些资源大小足足有600+kB
特别是 app.js。

这时就需要用到前面提到的 webpack-bundle-analyzer 去查看每个 bundle 的组成。后面发现,app.js 里包含了很多并不通用的资源,这些都是因为没有 Tree-Sharking 而被包含了进来,应该是因为我们使用的是 Next.js 12 版本,可能是使用的 webpack 4? 所以没有默认开启 Tree-Sharking。

所以,通过开启 Tree-Sharking 解决,在 package.json 里指定 Side effect


 "sideEffects": [
    "./src/components/templates/Builder/gfm.tsx"
  ],

开启 sideEffects 需要你对项目特别了解,需要将那些虽然没有被引用到但是实际有在使用的资源列出来,避免被 sharking 掉,而产生 bug。
具体可翻阅文档:https://webpack.js.org/guides/tree-shaking/。

另外,对几个依赖包进行了优化,比如使用lodash-esm 替换lodash,减少 buidler.io SDK 体积等。

另外,我觉得还有一个很关键的点,就是使用Lazy load 比如,登录弹窗等,就可以等用户点击了再去加载,在 Next.js 中可以方便的使用 Dynamic API 轻松实现Lazy load

最后First Load JS shared by All的体积减少了一半,只有 300+KB,这样每个页面需要加载的所有资源也会减少了,但依然还是飘红,有待进一步优化。

alt

最后得到的分数:

alt

FCP LCP 指标都得到了提升,然而还不是特别理想,还是有更多的机会去提高它。

后续的计划:

  • 上 CDN
  • 等等

有兴趣的同学可以一起探讨!