前言
最近两年都在搞shopify相关的二开。在headless建站方案被否之后,就陷入了无尽组件开发,样式堆积。再加上各种业务、投放的pixel,各种各样的问题吧,导致店铺的性能确实很拉。
这是优化之后的pagespeed移动端得分。仍然有可以提升的地方,但是对于shopify建站的店铺来说已经达到了第一梯队的性能评分
由于shopify本身对于性能已经做了很多优化,如ssg、cdn缓存、gzip、http2等,所以常用的一些优化手段并不适用于我们,那么在平台已经做了做够多工作的情况下,我们还能做些什么?
熟悉的同学应该知道影响评分的几项关键指标
- FCP -- 首个dom在页面中绘制的时间(白屏时间)
- LCP -- 首屏最大内容绘制时间 (最关键、 最直观影响网站体验)
- TBT -- 阻塞时间
这三个指标的优化可以说是老生常谈的问题了,其实核心思路可以拆分为以下几点
- 最小化阻塞资源(如: 基础样式表、基础js库等)
- 将关键信息的渲染时间提前
- 推迟非关键内容的加载和渲染
最小化阻塞资源
我们都知道css文件、非defer的js文件会阻塞页面的渲染。尤其是在spa项目中这个问题尤其严重。所以对于toC应用来说,你应优先使用SSR或SSG来构建应用。这样你就节省了第一步下载js,执行js到生成DOM所需的时间。
接下来我们来梳理一下哪些方面可能会阻塞你的页面渲染,然后针对问题逐一解决。
- 所有的css link(如果你没有做处理的话)
- 多个样式文件直接的相互覆盖导致的回流
- 所有的非defer js文件加载
- 执行js时遇到的long task(执行超过50ms)
- 过大的媒体资源
- 低效的cdn
1. css优化
这是一个常见的css link
<link href="example.css" rel="stylesheet" type="text/css" media="all">
media属性规定被链接文档将显示在什么设备上,利用这个属性就衍生出了很多优化方式。
- 比如将不同屏幕尺寸所需的css分开加载,可以缩小加载css文件的体积。
<link href="example-pc.css" rel="stylesheet" type="text/css" media="screen and (min-width: 768px)">
<link href="example-m.css" rel="stylesheet" type="text/css" media="screen and (max-width: 767px)">
- 尽可能的拆分主体样式文件,并将非关键的
css link的media属性设置为none
<link rel="stylesheet" href="footer.css" media="none" onload="this.media='all'">
当media属性为none时,浏览器会以最低优先级加载此文件,并不会阻塞页面的渲染进程。
这就对于你拆分css提出了要求,比如你将首屏所需的组件样式拆分出去后。那么此时浏览器会在低优加载后进行回流,那么网站的cls指标会变得惨不忍睹的同时对用户带来灾难性的体验。
当然,如果你的站点还没有升级的http2.0,那么这样细碎的拆分方式还是要慎重考虑。
- 对于主体样式的预加载
<link rel="preload" href="common.css" as="style">
2. js优化
我们网站的js可以大致分为两类:交互js和监控js,核心思路在于如何解放主线程。
2.1 交互js
其中交互js中可以区分为公共js库(如轮播图、动画库等)以及组件js
我们知道非defer的script标签会阻塞页面的渲染,而我们的页面是SSG的。所以接下来的优化思路就很清晰了 => 将js对于页面初始渲染的干预降到最小后,再以defer方式引入js
这就对于我们设计组件时提出了要求。
- 对于前两屏用到的高频组件,要尽量减少js操作dom的频率。
- 对于剩余页面的组件进行懒加载
2.2 监控js
toC的站点不可避免的会接入各种第三方的监控、分析的js文件。比如我们就接入了神策、fb、google、tiktok、klaviyo、pinterest等等。
每一个监控脚本的文档中都会告诉你请将它们的引入放在head标签中尽量靠上的位置以获取最准确的数据。当你这样做了之后你会发现network瀑布流中前几名的下载都是它们。
显然让用户更快的看到我们的页面更有价值。
简单来说你可以将它们以defer的放置在页面末尾。
2.2.1 service worker
总的来说这些监控js在做的就是收集收据,上报数据,对于页面并不是强关联的。这就天然的适合在service worker中加载、执行它们。你只需要在主线程中向worker线程发送一个任务,后续的所有操作占用主线程的时间都被解放了出来。这对于页面性能的提升是巨大的。
原理并不复杂、你可以手写或者引入partytown来搞定。
2.3 js长任务
本文只提供思路,针对于长任务的优化有很多文档说的很详细了,核心还是减少浏览器主线程的占用时间。
3. 媒体资源
3.1 图片
对于图片来说,可以使用webp、pjpg等来实现更快或渐进式的加载。avif也不错。当然,不要忘了使用srcset来自适应加载。
除了图片本身的大小,一个更广泛的场景是同一个img要在移动端和桌面端展示两张不同图片。
这里可以使用css的background-image,也可以用pictrue + source来实现
<picture>
<source
media="(max-width: 767px)"
srcset="mobile-image.webp?width=350 350w,
mobile-image.webp?width=550 550w,
mobile-image.webp?width=750 750w
"
>
<source media="(min-width: 768px)"
srcset="pc-image.webp?width=1200 1200w,
pc-image.webp?width=1500 1500w,
pc-image.webp?width=1800 1800w
"
>
<img loading="eager" src="base-img.jpg" >
</picture>
3.2 视频
对于视频的压缩没有怎么研究,只是找了一个在线压缩网站使用,欢迎讨论。 www.freeconvert.com
4. cdn预热
刚成立时,由于我们的网站访问量很少,图片和视频的加载速度并不理想。我们的方案是用python定时跑一遍网页来预热shopify的cdn,效果还是不错的。