在前端页面中,性能指标是常常被提及的话题,目前的指标通常由以下几个数据组成:
- 服务器响应时间:用户访问网站后需要多久时间才会获得响应。
- 页面渲染时间:页面完全可见以及可交互所需要的时间。
- 用户交互时间:用户在页面上进行关键交互的所需时间。(比如添加商品至购物车)
更详细的介绍可以参考以下两篇 Vercel 官方撰写的博客。
目标优化站点
通常在页面开发过程中,我们可以采用https://pagespeed.web.dev/或 Chrome 浏览器中自带的 Lighthouse 来进行页面性能的简单测试。
这是我们未优化的页面:
可以看到目前站点首屏的 Lighthouse 性能测试中,LCP(Largest Contentful Paint) 与 Speed Index 两项指标的表现并不是很好。
在进行全部的优化之前,先让我们看一下构建的日志结果。
不难看出,在构建结果中,首屏的页面大小(Size)与首次需要加载的 JS 大小(First Load JS)相对于其他的页面突出不少,而页面大小与 First Load JS 与 LCP(Largest Contentful Paint) 的分数息息相关。因此我们先对首屏的 JS 大小进行分析。
分析首屏 JS
站点使用 Next.js 进行构建,可以通过
@next/bundle-analyzer
包来进行 JS 捆绑包的分析。bun i @next/bundle-analyzer
在项目根目录下的
next.config.mjs
中进行分析的配置。import withBundleAnalyzer from '@next/bundle-analyzer'
const bundleAnalyzer = withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
experimental: {
optimisticClientCache: true,
swcMinify: true,
gzipSize: true,
taint: true,
},
}
export default bundleAnalyzer(nextConfig)
配置完成后,通过设置环境变量
ANALYZE
进行使用 bundle-analyzer 分析的构建:ANALYZE=true bun run build
在构建过程中,浏览器会自动打开三个页面。
- nodejs.html 显示服务器端的所有捆绑包。
- edge.html 显示在边缘计算中的所有捆绑包。
- client.html 显示客户端的所有捆绑包。
在本项目中,由于是采用静态部署,因此通过分析 client.html 即可。
首先在看看首页的组件构成:
主要由网络信息图表,订阅列表以及服务器列表构成。
在 client.html 中,筛选出首页:
分析页面上显示的捆绑包,所占的面积越大,表示捆绑包越大。
不难看出,recharts 包所占的空间不小,而此包主要用于网络速率显示图表中:
因此可以通过优化此部分的组件内包导入来优化 First Load JS。
优化包导入
在官方的最佳实践中,我们可以采用 Lazy Loading 的方式,尽可能延迟那些比较重的依赖包。
动态导入是其中的一种方式,原理十分简单,在初次获取页面时,并不会去获取需要动态导入的依赖包,只有在页面加载后,才会根据条件去进行获取依赖包,这对于大型网站的优化十分有用,比如需要用户点击后才会出现的图表,或者是需要登录后才会显示的一些用户组件,这些都是无需在第一次获取页面时就去下载的数据,因此都可以使用动态导入来进行优化。
那就着手优化,这是优化前的组价导入:
import NetworkChart from '@/components/NetworkChart'
将其改成动态导入,并且加上一个骨架进行占位显示:
import dynamic from 'next/dynamic'
const NetworkChart = dynamic(() => import('@/components/NetworkChart'), {
ssr: false,
loading: () => <Skeleton className="mt-2 h-[80px] w-full rounded-[10px]" />,
})
这样子,在页面初次加载的时候,就不用加载 recharts 依赖包,而是等到页面加载后,再进行下载,同时骨架使得页面的 Cumulative Layout Shift 保持较低的水平。
使用动态导入后,重新进行构建:
可以看到,Size 从 115kB → 7.63kB ,同时 FLJ 也从 267kB → 159kb,使用动态导入后优化效果十分显著。
重新进行 Lighthouse 的测试,可以发现 LCP 也从先前的 4.6s 优化到1.6s。
欢迎访问页面来试试首屏显示的速度。
结论
在优化首屏显示中,通过将一些比较重的依赖包采用动态导入的方式进行导入,不仅优化了页面大小也优化了初次加载 JS 大小,使得页面在初次加载的时候速度更快。