強制 nextjs app router 不使用 Static Site Generation (SSG)

nextjs 提供多種 render page 的方式,當中最特別的是 server side rendering, 甚至 static page generation,對於網頁瀏覽速度大有助益。

nextjs 當中可以分成 app route 以及 page route 兩種建置方式,以目前(nextjs v.14)來說兩者可以並存,而 nextjs 官方是建議如果是新的專案,或者是需要比較複雜 routing 處理的專案都建議用 app route。

nextjs pages router rendering

主要有分成三種: Static Site Generation (SSG), Server-side Rendering (SSR), Client-side Rendering (CSR)。大多數情況下這三者沒有太大差異,但在需要呼叫外部 api (會用到 getServerSideProps, getStaticProps等 data fetching 方式)取得資料的時候就會有很大的差別。可以參考 https://nextjs.org/docs/pages/building-your-application/rendering

當呼叫外部 API取得資料時,採用 CSR 的頁面在瀏覽器裡面開啟開發人員模式可以發現到呼叫的API,對於不想暴露 API endpoint 來說不是個好主意。採用 SSR 的話則是在 server side 去呼叫 API,因此在開發人員模式中不會看到API呼叫資訊。而採用 SSG,則會在建置過程(yarn build)時就先呼叫 API,產生靜態頁面,雖然加快了頁面讀取速度,但是帶來的影響就是當資料有改變時,這些靜態頁面就等於 out of date了。

nextjs app router rendering

如果是 app router,主要分成 server component & client component,其中 client component 可以視為類似CSR的方式。可以參考 https://nextjs.org/docs/app/building-your-application/rendering

使用 server component 的時候,則有分 static rendering, dynamic rendering, streaming 三種類型。當中的 static rendering 相當於 pages router 的 SSG,而 dynamic rendering 相當於 pages router 的 SSR。

如果沒有特別指定,app router 的 server component 會預設用 static rendering。如果強制要用 dynamic rendering,根據 https://stackoverflow.com/questions/76852746/how-to-opt-out-of-static-site-generation-in-next-js-13-with-approuter 所說,需要在頁面加上

export const dynamic = "force-dynamic";

修改前 (line#4 會有一堆抓 api 的 output)

 ✓ Linting and checking validity of types
 ✓ Collecting page data
   Generating static pages (1/9)  [==  ]
###一堆抓 API 資料
 ✓ Generating static pages (9/9)                                                                                                                                                        
 ✓ Collecting build traces
 ✓ Finalizing page optimization

Route (app)                               Size     First Load JS
┌ ○ /                                     608 B          84.9 kB
├ ○ /_not-found                           882 B          85.1 kB
├ λ /attending/edit/[id]                  1.93 kB        86.2 kB
├ λ /attending/index                      876 B          85.1 kB
├ ○ /attending/new                        1.72 kB          86 kB
├ λ /attending/search                     174 B          91.2 kB
└ ○ /contact1                             137 B          84.4 kB
+ First Load JS shared by all             84.3 kB
  ├ chunks/69-d21df144bc6d599f.js         29 kB
  ├ chunks/fd9d1056-17694c5d0d6a3a92.js   53.4 kB
  └ other shared chunks (total)           1.87 kB

Route (pages)                             Size     First Load JS
─ λ /api/delete-cookie                    0 B            78.9 kB
+ First Load JS shared by all             78.9 kB
  ├ chunks/framework-aec844d2ccbe7592.js  45.2 kB
  ├ chunks/main-0a23bf468cd5eaab.js       31.8 kB
  └ other shared chunks (total)           1.84 kB

○  (Static)   prerendered as static content
λ  (Dynamic)  server-rendered on demand using Node.js

Done in 10.47s.

修改後 (line #12 變成 dynamic rendering)

 ✓ Linting and checking validity of types
 ✓ Collecting page data
 ✓ Generating static pages (9/9)
 ✓ Collecting build traces
 ✓ Finalizing page optimization

Route (app)                               Size     First Load JS
┌ ○ /                                     608 B          84.9 kB
├ ○ /_not-found                           882 B          85.1 kB
├ λ /attending/edit/[id]                  1.93 kB        86.2 kB
├ λ /attending/index                      876 B          85.1 kB
├ λ /attending/new                        1.72 kB          86 kB
├ λ /attending/search                     174 B          91.2 kB
└ ○ /contact1                             137 B          84.4 kB
+ First Load JS shared by all             84.3 kB
  ├ chunks/69-d21df144bc6d599f.js         29 kB
  ├ chunks/fd9d1056-17694c5d0d6a3a92.js   53.4 kB
  └ other shared chunks (total)           1.87 kB

Route (pages)                             Size     First Load JS
─ λ /api/delete-cookie                    0 B            78.9 kB
+ First Load JS shared by all             78.9 kB
  ├ chunks/framework-aec844d2ccbe7592.js  45.2 kB
  ├ chunks/main-0a23bf468cd5eaab.js       31.8 kB
  └ other shared chunks (total)           1.84 kB

○  (Static)   prerendered as static content
λ  (Dynamic)  server-rendered on demand using Node.js

Done in 16.53s.