optimize next js bundle size and performance
最近將這篇 zettelkasten card and blog static website generator 提到自己寫的部落格產生工具,做了一些 UI 優化還有 bugs 修正, 偶爾信箱還會收到一些不認識的人說要幫我提到 SEO 排名,想也知道是要收費的,不過我想想我這個只是個人網站似乎不太需要特別把SEO用到多好,但是我還是稍微做了一下功課優化,知道爲什麼嗎~
因爲萬惡的 google search console 對於他會不會把你的網站納入google search裡面的結果,裡面衆多可能的條件之一就是你的頁面 SEO 如果評分不夠好,是很有可能被拿掉,但是我覺得其實有其他佔比很重因素,只要它認爲你的內容沒價值(一個完全不知道標準爲何),它才不管你的網站優化多好,SEO做得多好,照樣拿掉 lol 不過想了想,不管如何,我多少還是做一下比較好,因爲有看過有人分享一些頁面被拿掉後,就再也加不回去,不管他做了多少修正,儘量減少被拿掉的因素!
當前網站評分
其實我之前有對於 image 的部分做了點優化,可以看看這篇 next image export optimizer with blur image in data url format, 圖片對於SEO的分數佔了不小的部分,因爲圖片通常是佔據網路下載用量的大部分~ 下面是目前用 pagespeed insights做的測試評分
其實就桌面環境來講,好像也沒有太多地方需要優化 lol 有不少地方其實是 next js 這個 framework 就有做不少地方的優化,可以看看官網,除了圖片之外接著就是一些bundle javascript的library可以看看怎麼優化大小了。
但是老話一句,我覺得這種優化大小,也都會隨著網路速度繼續進步的時代慢慢不重要,比如每秒10MB的速度,你是有多少 static assets 要下載? 正常有這種速度,頁面一進入老早開始進行 render 動作了,不會在卡在loading資源的階段了,不過現在高速網路還沒那麼普及,我們還是得來處理一下 bundle size。
優化 bundle size
https://nextjs.org/docs/app/building-your-application/optimizing/package-bundling#analyzing-javascript-bundles 官網有介紹怎麼使用,安裝並執行一下,應該會看到 terminal 類似下面這樣的針對各個route分析
Route (app) Size First Load JS
┌ ○ / 3.31 kB 119 kB
├ ○ /_not-found 871 B 88.1 kB
├ ○ /about 181 B 100 kB
├ ○ /blog 48.1 kB 163 kB
├ ● /blog/[slug] 1.26 kB 108 kB
├ ├ /blog/books_read_in_2024
├ ├ /blog/custom_sender_name_with_gmail_smtp_in_nodemailer
├ ├ /blog/coding_with_aider_caht
├ └ [+109 more paths]
├ ○ /blog/sitemap.xml 0 B 0 B
├ ○ /cv 137 B 87.4 kB
├ ○ /icon.png 0 B 0 B
└ ○ /sitemap.xml 0 B 0 B
+ First Load JS shared by all 87.2 kB
├ chunks/23-600be35fb9cf100a.js 31.6 kB
├ chunks/fd9d1056-c4050f5b61ad0a3b.js 53.6 kB
└ other shared chunks (total) 1.96 kB
○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses getStaticProps)
我們主要是要儘量降低 first load JS, 因爲這個一開始進入 route 就要先 load 好的,才會完整頁面渲染
另外其實瀏覽器也會自動打開分析圖,以我的例子我只需要看 client 端就好,如下圖
他可以搜尋 bundle 的 js 裡面用到的 library, 這裡我就發現我的 blog notfound page 多重複引用了 canva lite
其他我看到還有D3 library佔了不小,要怎麼優化呢?
思路
一個最極端的做法,如果今天你用到 D3 的地方不多,你可以考慮用 pure js 自己搞,這絕對是最大減少size的方法,或者找其他可以做到類似效用比較輕量的library,通常我會用這個網站 https://bundlephobia.com/ 來比較bundle後的大小。
今天我的部落格的確不太需要D3,我只是拿來做 blog 展示 zettelkasten 的節點渲染,像下圖
所以我有沒有在一開始就loading完D3,並不重要,我只要點擊打開再loading就夠了,但是我又懶得找其他的library,還有什麼做法呢?
你可能會想難道沒有 lazy loading 的方法嗎? 這時候 next js 的 dynamic import 就會說 「我這不是來了嗎」
爲了要減少 D3 在 first load JS 的 size,這邊特別使用了 next js 的 dynamic import,不過一開始使用都發現怎樣減少不了 first load js 的 size,加上看到有些人文章說也是一樣狀況,讓我不禁懷疑,這~真的有用嗎? 突然有個謎之聲 import 一次不行,就 import 第二次啊~ 我趕快甩甩頭,繼續找原因,後來發現不少人反應這個問題! 看看這個 issue ,算是容易誤踩的地雷? https://github.com/vercel/next.js/issues/49454
簡單說其實正確的用法是(這邊不得不說官方文檔寫得。。。 沒容易讓人搞懂用法),裡面討論串最後有人講到解法是
The quick way to make code splitting work is to use next/dynamic in client components.
也就是說你的 dynamic import 不應該在 server component 去 call 這個,而是另外寫一個 client component 去 dynamic import 你想要的 target component。 雖然有點繞就是了,我一直以爲 server component會自己看到 dynamic import 後,就會自己 split code 咧~
我這樣額外包裝後
某個 client side component
const DynamicGraphVisualization = dynamic(() => import('@/components/graph'), {
loading: () => <p>Loading...</p>,
ssr: false,
})
export const LazyTechExpansion = ({
blinks,
}: GraphVisualizationProps) => {
return (
<TechExpansion
ContentComponent={
<DynamicGraphVisualization
blinks={blinks}
/>
}
/>
)
}
然後 server side component 再引用 LazyTechExpansion,確實size有變少,可以看看 /blog 這個 route
Route (app) Size First Load JS
┌ ○ / 3.75 kB 119 kB
├ ○ /_not-found 871 B 88.2 kB
├ ○ /about 182 B 101 kB
├ ○ /blog 22.9 kB 138 kB
├ ● /blog/[slug] 1.26 kB 109 kB
├ ├ /blog/books_read_in_2024
├ ├ /blog/custom_sender_name_with_gmail_smtp_in_nodemailer
├ ├ /blog/coding_with_aider_caht
├ └ [+109 more paths]
├ ○ /blog/sitemap.xml 0 B 0 B
├ ○ /cv 137 B 87.4 kB
├ ○ /icon.png 0 B 0 B
└ ○ /sitemap.xml 0 B 0 B
+ First Load JS shared by all 87.3 kB
├ chunks/23-600be35fb9cf100a.js 31.6 kB
├ chunks/fd9d1056-c4050f5b61ad0a3b.js 53.6 kB
└ other shared chunks (total) 2.02 kB
○ (Static) prerendered as static content
● (SSG) prerendered as static HTML (uses getStaticProps)
其實以我 bundle size 並不算大,因爲絕大部分都是因爲 NextJS + React 太肥~ 所以這邊我就不追求極端小的size了。
下面講講一些 next js 特別的東西
next link prefetch
https://nextjs.org/docs/pages/api-reference/components/link#prefetch 預設next js的 link 是會進行 prefetch 的,也就是說一旦link進入畫面(viewport),就會先去背後loading好資料,這個我覺得需要注意一下,如果你有很多link跟資料也不小,這有可能拖慢你的頁面顯示,所以看你的狀況可以設定成 false
https://nextjs.org/docs/app/api-reference/components/link#prefetch
不過 nextjs 有些恐怖地方是,根據你專案用 app route
還是 page route
行爲會有不一樣。。 就以這個 link prefech 設定成 false 一個是在 hover 後還是會 load 資料,另外一個是不會~ 其實還有很多地方~ 只能說官方文檔用力看 lol
next js 還有很多東西,只是目前用不太到,以後有用到再額外來寫一篇。
下一步
經過一些優化後,清理一些技術債後,我想差不多可以繼續下一步幫我的部落格產生器加一下新玩意了~ 當前應該還是會先加強一些頁面UI,還有CV頁面一直沒想到怎呈現比較好
另外之後預計是撰寫預覽體驗,雖然也可能不是那麼必要啦,目前很大部分都是直接在emacs直接寫完文章然後才build看成效的,對我來講算是很夠用了,不太需要即時預覽,反而是我有時候 突然想到如果要在手機端進行寫作的話呢? 似乎就不是那麼方便了,但是這個工程可能也會很大,姑且只是先想一下而已 :)