NextJS 首屏加载优化

1 年前(已编辑)
编程 /
2426
3
这篇文章上次修改于 1 年前,可能部分内容已经不适用,如有疑问可询问作者。

迭代 Kami 已经很长时间。中间也经过几次大的重构,但一直没有对首屏优化过。首屏加载速度很大程度被打包产物的体积所影响。在 Google Lighthouse 的 Performance 中也有一项建议是 Remove unused JavaScript。

在前段日子我发现 NextJS 虽然会进行 Tree Shake,但并不会细化到每个 Page 粒度的拆分多个文件,比如说你的项目总使用了 lodash-es 会进行 Tree Shake,但是他只是移除了你整个项目中未用到的方法,比如在 Page A 使用了 omit 方法,在 Page B 使用了 throttle 方法,由于都是使用 import {} from 'lodash-es' 导入,导致在 Page A 加载时也会去加载 Page B 的 throttle 方法。

对于首屏来说,这是一个极大的影响,如果能精确细化成一个个文件,首次载入的体积将大大减少。在优化之前,可以看到 /_app 非常的大。300K 出头的基础包_app.js。入口 / 311K。

info  - Collecting page data  
Page                                       Size     First Load JS
┌ λ /                                      5.17 kB         311 kB
├   └ css/653a178efa4724c9.css             950 B
├   /_app                                  0 B             304 kB
├ λ /[page]                                1.06 kB         329 kB
├   └ css/0675faafb467c2b9.css             1.66 kB
├ λ /404                                   692 B           305 kB
├ λ /api/bilibili/bangumi                  0 B             304 kB
├ λ /api/bilibili/cover                    0 B             304 kB
├ λ /api/bilibili/video                    0 B             304 kB
├ λ /api/feed                              0 B             304 kB
├ λ /api/netease/music                     0 B             304 kB
├ λ /api/netease/song                      0 B             304 kB
├ λ /api/sitemap                           0 B             304 kB
├ λ /category/[slug]                       7.89 kB         316 kB
├ λ /dev/zoom                              365 B           304 kB
├ λ /favorite/bangumi                      1.87 kB         310 kB
├ λ /favorite/music                        2.14 kB         310 kB
├   └ css/faba63f7732fd70f.css             573 B
├ λ /friends                               9.94 kB         326 kB
├   └ css/28a312a38641b4e6.css             489 B
├ λ /login                                 2 kB            306 kB
├   └ css/8cba0370ecaeebe2.css             521 B
├ λ /notes/[id]                            5.47 kB         333 kB
├   └ css/d8e0a9b1015a3d50.css             1.93 kB
├ λ /posts                                 6.94 kB         315 kB
├   └ css/e7a1dce72d581d85.css             755 B
├ λ /posts/[category]/[slug]               3.76 kB         332 kB
├   └ css/460cb4c7f44c400f.css             1.74 kB
├ λ /projects                              1.99 kB         310 kB
├ λ /projects/[id]                         2.29 kB         310 kB
├ λ /recently                              3.46 kB         307 kB
├   └ css/36c2d720f9b8ac99.css             636 B
├ λ /says                                  18.4 kB         326 kB
├   └ css/3d0401d6d411fe80.css             369 B
└ λ /timeline                              8.57 kB         316 kB
    └ css/711ec0198f2688fe.css             395 B
+ First Load JS shared by all              304 kB
  ├ chunks/framework-774fc966c0f2820d.js   42.2 kB
  ├ chunks/main-4dbd8084f1abd7f8.js        32.6 kB
  ├ chunks/pages/_app-e73bc29159e27c50.js  227 kB
  ├ chunks/webpack-a3fd8ab904e44f8c.js     1.77 kB
  └ css/b85e767cb9b1e73f.css               18.4 kB

由于 NextJS 12 目前并不支持 Layout(Layout 正在 RFC 中有望近期与我们见面),所以 _app.tsx 一般都是布局相关的东西。布局涉及到大量导入,组件和外部库。首先还是需要把 import 细化。比如修改 import { merge } from 'lodash-es'import merge from 'lodash-es/merge',细化 utils 导入,import { someFunction } from '~/utils'`import { someFunction } from '~/utils/someFunction' 。这是一件非常枯燥的事情。

第二,使用组件懒加载,在 React 18 和 NextJS 12 中,可以使用 React.lazy 和 Suspense 包装。在原先 BasicLayout 中,所有导入都是同步的,在加载这个布局是就需要同时去渲染大量额外的组件,可能这些不是 Content 的重要内容,如 Header Footer Sider 都是可以异步延迟去渲染的。


image-20220624143031035

然后 Lazy 的组件用 React 18 新特征 Suspense 进行包装。只留重要的 Content 部分。细化之后,React 会注重渲染 Content 部分,再是周边组件。遇上弱网,可以很明显的对比效果。


image-20220624143220837

再者,移除 NextJS 提供的对老旧浏览器的垫片。在 next.config.js 添加。

 experimental: {
    legacyBrowsers: false,
    browsersListForSwc: true,
  },

通过以上的优化,_app.js 已经很大程度减少了体积。#1245


image-20220624143826058

继续对主页面,进入主站的第一站也就是 pages/index.tsx 重复上面细化 import 的步骤进行优化。#1292


image-20220624143946413

经过优化,在原先的全局体积 300K 优化到了基础包 171K。比原先将近少了一半。

通过 Google Lighthouse Perfermance 跑分。首屏高达 93 分。


image-20220624144301180
评论区加载中...