跳至內容
返回部落格

2019 年 9 月 30 日,星期一

Next.js 9.0.7

發佈者

Next.js 9.0 大約兩個月前發佈了。從那時起,我們一直忙於 7 個較小但相當重要的版本:9.0.19.0.29.0.39.0.49.0.59.0.6 以及 9.0.7

讓我們深入了解這些版本為您的網站和應用程式帶來了哪些更新,而且完全沒有任何破壞性變更。

在 Windows 環境中提升並行處理能力

Next.js 在 next build 過程中會在許多地方進行並行處理。主要用途是使用 Terser 平行地縮小建置輸出。

先前,這項工作是透過一個名為 worker-farm 的套件,跨多個 CPU 進行處理。然而,我們注意到許多 Windows 使用者透過自訂 webpack 設定停用了縮小功能。經過進一步檢查,我們發現 worker-farm 在 Windows 電腦上無法穩定運作。

為了要解決這個問題,我們已將 worker-farm 遷移到 jest-worker。這確保了在 macOS、Linux 和 Windows 電腦上的建置都能可靠且一致。

顧名思義,jest-worker 是 Jest 測試執行器的一部分。它是 Jest 用來平行化測試案例的套件。這代表這個套件經過了大量的實戰測試,可靠且持續維護。

jest-worker 也支援 worker_threads,這是 Node 12 的新功能。與 child_process 不同,worker_threads 可以共享記憶體,這代表在新版 Node 上能有更快的建置速度。

藉由切換到 jest-worker,我們能夠重新啟用 Windows 使用者的建置並行處理功能。

預設啟用 Gzip 壓縮

在調查公司為何使用自訂伺服器時,我們發現最常見的原因是為了壓縮。公司會新增一個名為 compression 的 Express 中介軟體,它負責處理 HTTP 回應的 Gzip 壓縮。

這個中介軟體會壓縮回應,讓傳輸給使用者的位元組數更少。通常,這應該由像 Nginx 這樣的反向代理伺服器來處理。反向代理伺服器會將 CPU 密集的工作從單執行緒的 Node 程序中移除。

然而,在檢查網路上的 Next.js 使用情況時,我們發現很大比例的公司沒有設定任何壓縮。

在像 Vercel 這樣的平台上,gzipbrotli 會在代理伺服器層級自動處理。

如果是自行代管,公司就必須自行新增 gzip(透過 compression 或反向代理伺服器)。

從 Next.js 9.0.4 開始,使用 next start 或自訂 server.js 時,預設會包含 gzip 壓縮。

brotli 的支援即將推出即將推出,因為 Node.js 現在原生支援 Brotli。

如果您的應用程式已透過自訂伺服器啟用壓縮功能,Next.js 不會新增其自身的壓縮器。

Next.js 預設不包含針對無伺服器目標的壓縮功能,因為使用無伺服器目標時,資源是單獨上傳的,而不是透過 Node.js 提供。

如果您部署在處理壓縮的平台上,例如 Vercel,則無需進行任何變更。

僅針對使用中的頁面進行 TypeScript 報告

Next.js 9 內建了對 TypeScript 的支援。只需將單個頁面的副檔名從 .js 重新命名為 .tsx 即可。Next.js 會自動處理或引導您完成其餘的設定。

Next.js 也會透過與開發流程並行執行 tsc --watch 來處理類型檢查。在開發過程中,Next.js 有一個稱為隨選項目 的概念。此功能僅編譯您正在積極處理的頁面。

The on-demand entries flow
隨選項目的流程

從 9.0.4 版開始,Next.js 現在只會報告正在被隨選項目積極編譯的頁面的類型錯誤。這減少了大量的類型檢查雜訊,同時專注於特定頁面集。

完整的應用程式類型檢查仍然會在 next build 期間執行,或者可以在您的編輯器中/由您的編輯器處理。

遙測 vercel.comvercel.com/docs 以及 https://nextjs.dev.org.tw。我們一直在 Vercel 內部試用(dogfooding) Next.js,並根據我們的經驗改進 Next.js。

除此之外,我們還積極與社群互動以收集意見回饋。您可能之前曾與 Tim 交流,提供您在公司如何使用 Next.js 的意見回饋。

例如,如果您使用自訂伺服器、自訂 webpack 設定等等。這些意見回饋對於引導 Next.js 的功能開發極具價值。

然而,這種方法存在一個問題,那就是我們只能從一部分使用者收集意見回饋。這部分使用者的需求和使用案例可能與您/貴公司不同。

舉例來說,匯入 CSS 檔案是一種非標準的做法,但相當多的使用者似乎正在使用這種做法,無論是透過 next-css(或 Sass/Less),還是自訂設定。如果我們知道有多少百分比的使用者正在使用該特定方法,我們就可以優先改進它。

基於這個原因,我們引入了一種匿名的、更加自動化的方法來收集這些意見回饋點,以便我們在不久的將來更好地改進 Next.js。

這也將使我們能夠驗證對框架所做的改進是否正在改善所有應用程式的基準。

您可以在 nextjs.org/telemetry 上閱讀更多關於遙測的資訊。在那裡,您還會找到如何在不想參與的情況下選擇退出。

建置回饋,以點號指示活動

在與 Next.js 使用者交談時,一個小小的意見回饋是,有時看起來 next build 沒有做任何事情,主要是在視覺上。

為了要解決這個問題,我們在執行 next build 時,在控制台輸出中加入了一個載入指示器。這個輸出會在視覺上顯示指令仍在執行中,而且程序沒有凍結。

我們計劃在可能的情況下,擴展此建置輸出以顯示更多建置階段。

新的建置指示點號

改進的 next/head 元素追蹤

Next.js 提供了內建的方法來管理 <head> 元素,因為它負責渲染應用程式的 HTML。這個 API 透過 next/head 模組公開。

例如,要新增頁面標題:

pages/index.js
import Head from 'next/head';
 
export default function MyPage() {
  return (
    <>
      <Head>
        <title>My Title</title>
      </Head>
      <h1>Hello World</h1>
    </>
  );
}

在渲染成 HTML 時,Next.js 會收集所有在 <Head> 中渲染的組件,並將標籤渲染到頁面的 <head> 中。

然而,Next.js 允許使用 <Link> 組件進行單頁應用程式 (SPA) 類型的路由轉換。

當您點擊 <Link> 時,Next.js 會擷取所需的 JavaScript 檔案,以便在客戶端渲染頁面。然後,它會渲染與該檔案關聯的 React 組件。

由於此轉換發生在客戶端,我們必須清除前一頁面注入的 <head> 元素,否則過時的元素可能會出現在其他頁面上。

以前,Next.js 透過向每個 <Head> 提供的元素添加類別名稱來追蹤這些元素。

以上述範例為例,<head> 看起來會像這樣:

<head>
  <title class="next-head">My Title</title>
</head>

這個解決方案效果很好,因為每個透過 next/head 注入的元素都被清楚地標記,並且易於清除。

然而,一小部分使用者回報了元素上的額外 class 屬性有時會導致從外部服務添加的腳本無法驗證的問題。

來自 Google Chrome 團隊的 Gerald Monaco 提出了一種方法,可以在不需在元素上使用類別名稱的情況下保留清除行為。

新的行為是 Next.js 會插入一個額外的 <meta> 標籤,其中包含它 (next/head) 渲染的元素數量。藉此,Next.js 可以使用計數來清除現有元素。

因此,這種方法可以減少在頁面的 <head> 中注入多個元素時的初始 HTML 負載大小。

防止在 Pages 目錄中出現非頁面檔案

開始使用 Next.js 時,您要做的第一件事就是建立 pages 目錄。

慣例是 pages 目錄中的每個檔案都會成為應用程式中的一個路由。一個簡單的例子是 pages/about.js 會變成 /about

隨著時間推移,我們偶爾會收到使用者回報,無論應用程式規模大小,建置效能都很差。

經過進一步調查,發現這些使用者在 pages 目錄中建立了整個組件結構。

由於 pages 目錄中的每個檔案都被視為一個頁面,應用程式中的每個組件都被編譯成一個頁面。這會造成大量的建置時間開銷,為無效的頁面產生兩個以上的 JavaScript 檔案。

此外,這會部分影響 Next.js 決定如何產生程式碼分割區塊的方式。這是因為 Next.js 使用關於跨頁面程式庫使用的啟發式方法。

因此,我們必須確保使用者不會將此陷阱引入他們的 Next.js 應用程式。

Next.js 9 現在會驗證 pages 目錄中的檔案是否匯出 React 組件。

實際上,這表示 Next.js 會顯示一條訊息,提醒您在 pages 目錄中找到了一個潛在的非頁面檔案。

這會鼓勵使用者將不是頁面的檔案移到另一個目錄。進而使開發、生產和程式碼分割更快、更準確。

執行時間改進

Next.js 框架由許多部分組成。其中之一是客戶端執行時間。此執行時間處理 hydration、客戶端路由等。

Hydration,也就是這次改進的重點,是讓伺服器渲染或預渲染的 HTML 互動的必要條件。Hydration 會新增事件處理程式並呼叫生命週期方法,例如 useEffect()componentDidMount,讓您的應用程式為最終使用者做好準備。

此外,Next.js 處理的不僅是基本的 hydration,例如設定客戶端路由器、配置 next/head,以及透過 next/dynamic 載入額外的應用程式邏輯。

這些職責也各自有其特定的執行時間部分。

next/dynamic 為例,Next.js 必須確保在伺服器上渲染的延遲載入組件在客戶端上也準備就緒。每次使用 next/dynamic 都會產生一個額外的 JavaScript 捆綁包,這些檔案必須在 hydration 之前載入,以避免 hydration 不匹配。

以前,這個執行時間會始終包含在 Next.js 執行時間捆綁包中。現在,只有在您的應用程式中使用 next/dynamic 時才會包含它。這意味著對於不使用 next/dynamic 的應用程式,下載、解析和執行的 JavaScript 程式碼更少。

AppTree 支援

React 生態系統中的一些程式庫以非常特定的方式實現伺服器端渲染。最值得注意的是,Apollo 的伺服器端渲染解決方案,稱為 getDataFromTree,其工作方式是渲染 React 樹,並且對於找到的每個 Query,它都會等待結果,然後再次重新渲染 React 樹。

預設情況下,Next.js 會將一些上下文值新增到 React 樹中,例如可以使用 useRouter 讀取的路由器。

with-apollo 範例過去渲染 React 樹的方式是透過渲染 <App> 並嘗試手動填入缺少的屬性。隨著 React Context 的加入,這不再可行,因為上下文提供者是一個單獨的元素。

從 Next.js 9.0.4 開始,在 getInitialProps 的上下文物件中新增了一個名為 AppTree 的新屬性。它是專門為外部程式庫必須遍歷整個 React 樹的情況新增的,例如 Apollo 的 getDataFromTree

with-apollo 範例已 更新以反映這些變更。如果您應用程式中已實作 Apollo,建議您更新至 AppTree 的方法,以便 useRouter 和未來新增的其他 API 可以在您的 Next.js 應用程式中正常運作。

如果您沒有使用 Apollo 或類似的函式庫,我們建議您盡量避免使用 AppTree,因為 Next.js 團隊通常不建議遍歷 React 樹狀結構。這會增加相當多的效能負擔,因為 React 樹狀結構會被渲染多次,而不是只渲染一次。

移除 next-server 套件

一年多前,當我們開始針對無伺服器部署最佳化 Next.js 時,我們建立了一個名為 next-server 的套件。這個套件是實驗性的,並與 next 套件一起發佈。它從未公開記錄,但卻是一個建立最小 Next.js 伺服器執行環境的實驗。

最終,該套件獲得了成功,確實縮小了生產伺服器的執行環境。然而,我們想出了另一種創新的方法,透過 Next.js 編譯器和 靜態分析,讓執行環境更小。

如此一來,next-server 就變得過時了,並被 無伺服器目標 所取代。與使用 next-server 套件取代 next 相比,這個目標的輸出最佳化程度更高。

雖然這個套件已經過時,無法直接使用,但我們還是保留了它。這是因為它有一些套件之間共用的內部程式碼,而移動這些程式碼需要相當大的工作量。

我們最近完成了這項工作,並將程式碼從 next-server 移回 next 套件中。這意味著 Next.js 框架的所有程式碼現在都位於 next 套件中。

這讓新手和經驗豐富的貢獻者都能更容易地為 Next.js 做出貢獻。現在只有一個編譯過程和統一的建置設定。以前,nextnext-server 有不同的設定,以及對每個套件中程式碼歸屬的任意限制。

升級 Next.js

如果您的專案使用的是舊版 Next.js,我們建議您升級到 Next.js 9。

大多數情況下,升級不需要進行任何更改。您可以參考升級指南以確保升級過程順利。

我們要感謝所有社群貢獻者自發布以來對指南的更新。

未來的發展方向為何?

這篇部落格文章中概述的新優化只是我們一直在努力的更廣泛優化和功能的開始。

我們很快就會分享正在進行的 RFC 的更新。在此之前,您可以透過 GitHub 上的 RFC 標籤 搶先了解一下。

這顯示了我們一直在研究的一些功能,例如內建 CSS 支援public 資料夾支援,以及src 資料夾支援

社群

我們很高興看到 Next.js 社群持續成長。

  • 我們有超過 800 位貢獻者,每位至少提交了 1 次 commit。
  • 在 GitHub 上,該專案已獲得超過 41,100 個星標。

自上次主要版本發佈以來,Next.js 社群已擴大一倍,成員超過 10,900 人。加入我們!

我們很高興看到社群持續的貢獻以及來自公司和用戶的外部回饋,這些都有助於塑造我們的版本發佈。