跳到內容
返回部落格

2020 年 7 月 27 日 星期一

Next.js 9.5

作者:

我們很高興在今天推出 Next.js 9.5,其特色功能包含

穩定的增量靜態重新產生

Next.js 在 9.3 版本中引入了靜態網站產生方法,目標明確:我們應該獲得靜態的優點(始終快速、始終在線、全球複製),同時對動態資料提供絕佳的支援,這也是 Next.js 聞名的特色。

為了兼顧兩者的優點,Next.js 引入了增量靜態產生 (Incremental Static Generation),在您已經建置網站後更新靜態內容。透過在 getStaticPaths 中使用 fallback: true 選項,您可以執行階段註冊新的靜態頁面

Next.js 可以透過這種方式靜態預先渲染無限數量的頁面,隨需應變,無論您的資料集有多大。

今天,我們宣布增量靜態重新產生 (Incremental Static Re-generation)正式全面推出,這是一種更新現有頁面的機制,透過在背景中重新渲染頁面,隨著流量的進入而更新。

受到 stale-while-revalidate 的啟發,背景重新產生確保流量不間斷地提供服務,始終來自靜態儲存,並且僅在完成產生後才推送新建立的頁面。

export async function getStaticProps() {
  return {
    props: await getDataFromCMS(),
    // we will attempt to re-generate the page:
    // - when a request comes in
    // - at most once every second
    revalidate: 1,
  };
}

revalidate 標記是以秒為單位的時間,在此期間最多發生一次產生,以防止 https://en.wikipedia.org/wiki/Cache_stampede

與傳統 SSR 不同,增量靜態重新產生確保您保留靜態的優點

  • 延遲時間不會飆升。頁面以一致的速度快速提供服務。
  • 頁面永遠不會離線。如果背景頁面重新產生失敗,舊頁面仍保持不變。
  • 資料庫和後端負載低。頁面最多只會「同時」重新計算一次。

增量功能(新增頁面和延遲更新頁面)以及預覽模式現在都已穩定,並且已完全支援 next startVercel edge 平台

為了展示這項新功能,我們建立了一個範例,展示重新產生靜態頁面,該頁面顯示特定議題的各種 GitHub 反應計數:https://reactions-demo.vercel.app/

After the first visit following our emoji reaction, a new page generation kicks off in the background. Every single request throughout is served from static cache.
在第一次造訪後,隨著我們的表情符號反應,新的頁面產生會在背景中開始。整個過程中的每個請求都從靜態快取提供服務。

接下來,我們將致力於補充 RFC,以解決另外兩項增量靜態產生功能

  • 一次重新產生和失效多個頁面(例如您的部落格索引和特定的部落格文章)
  • 透過監聽事件(例如 CMS Webhook)在使用者流量之前重新產生

如需更多詳細資訊,請查看 getStaticProps 文件

可自訂基礎路徑

Next.js 專案並非總是從網域的根目錄提供服務。有時您可能希望將 Next.js 專案託管在子路徑下,例如 /docs,以便 Next.js 專案僅涵蓋網域的該子區段。

雖然到目前為止這是可能的,但卻犧牲了相當多的額外設定。例如,將前綴新增到每個 <Link>,並確保 Next.js 從正確的路徑提供 JavaScript 套件。

為了應對這個痛點,我們正在引入一個新的設定選項。basePath 讓您可以輕鬆地在您網域的子路徑上託管您的 Next.js 專案。

若要開始使用 basePath,您可以將其新增至 next.config.js

next.config.js
module.exports = {
  basePath: '/docs',
};

設定 basePath 後,您的專案將自動從提供的路徑路由。在本例中為 /docs

當使用 next/linknext/router 連結到專案中的其他頁面時,basePath 將會自動加上前綴。這讓您可以在不變更專案的情況下變更 basePath

使用 next/link 路由到另一個頁面就是一個例子

import Link from 'next/link';
 
export default function HomePage() {
  return (
    <>
      <Link href="/documentation-page">
        <a>Documentation page</a>
      </Link>
    </>
  );
}

以這種方式使用 next/link 將導致以下 HTML 渲染到網頁瀏覽器

<a href="/docs/documentation-page">Documentation page</a>

如需更多詳細資訊,請查看 basePath 文件

支援重寫、重新導向和標頭

重寫

在建置 Next.js 專案時,您可能想要將某些路由代理到另一個 URL。例如,如果您想要逐步將 Next.js 導入您的堆疊,您會想要路由存在於您的 Next.js 專案中的頁面,然後將所有未比對到的內容路由到您要遷移離開的舊專案。

在 Next.js 9.5 中,我們引入了一個名為 rewrites 的新設定選項,讓您可以將傳入的請求路徑對應到不同的目標路徑,包括外部 URL。

例如,您可能想要將某個路由重寫為 example.com

next.config.js
module.exports = {
  async rewrites() {
    return [
      { source: '/backend/:path*', destination: 'https://example.com/:path*' },
    ];
  },
};

在這種情況下,/backend 下的所有路徑都將路由到 example.com

您也可以檢查您的 Next.js 專案路由是否比對到,然後在沒有比對到的情況下重寫為先前的專案。這對於 Next.js 的逐步採用非常有用

module.exports = {
  async rewrites() {
    return [
      // check if Next.js project routes match before we attempt proxying
      {
        source: '/:path*',
        destination: '/:path*',
      },
      {
        source: '/:path*',
        destination: `https://example.com/:path*`,
      },
    ];
  },
};

在這種情況下,我們先比對所有路徑。如果沒有任何比對,我們會代理到 example.com,這將是先前的專案。

若要深入了解 rewrites 功能,請查看 rewrites 文件

重新導向

大多數網站至少需要一些重新導向。尤其是在變更專案路由的結構時。例如,當將 /blog 移動到 /news 或類似的轉換時。

先前在您的 Next.js 專案中擁有重新導向清單需要設定自訂伺服器或自訂 _error 頁面,以檢查路由是否設定了重新導向。然而,這會以無效化關鍵靜態和無伺服器最佳化(透過擁有伺服器)為代價,或者不夠符合人體工學。

從 Next.js 9.5 開始,您現在可以在 next.config.js 中的 redirects 金鑰下建立重新導向清單

next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
    ];
  },
};

若要深入了解 redirects 功能,請查看 redirects 文件

標頭

Next.js 讓您可以建置混合專案,同時使用靜態產生和伺服器端渲染。透過伺服器端渲染,您可以為傳入的請求設定標頭。對於靜態頁面,直到現在才有可能設定標頭。

我們現在在 next.config.js 中引入了一個 headers 屬性,該屬性適用於所有 Next.js 路由

next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Feature-Policy',
            // Disable microphone and geolocation
            value: "microphone 'none'; geolocation 'none'",
          },
        ],
      },
    ];
  },
};

headers 選項讓您可以設定常用的標頭,例如 Feature-PolicyContent-Security-Policy

若要深入了解 headers 功能,請查看 headers 文件

網址中可選的尾部斜線

當 3 年前推出 Next.js 時,其預設行為是所有帶有尾部斜線的 URL 始終返回 404 頁面。

雖然有效,但有些使用者要求能夠變更此行為。例如,當將現有專案遷移到先前始終強制執行尾部斜線的 Next.js 時。

在 Next.js 9.5 中,我們在 next.config.js 中引入了一個名為 trailingSlash 的新選項。

這個新選項確保 Next.js 自動處理尾部斜線行為

  • 自動將帶有尾部斜線的 URL 重新導向到不帶尾部斜線的 URL,例如:/about//about
  • trailingSlash 設定為 true 時,不帶尾部斜線的 URL 將重新導向到帶有尾部斜線的 URL,例如:/about/about/
  • 確保 next/link 自動套用/移除尾部斜線,以避免不必要的重新導向。
next.config.js
module.exports = {
  // Force a trailing slash, the default value is no trailing slash (false)
  trailingSlash: true,
};

若要深入了解 trailingSlash 功能,請查看 trailingSlash 文件

頁面套件的持久快取

在編寫 Next.js 頁面時,所有指令碼套件、CSS 樣式表和 HTML 的建立都是完全自動化且從您那裡抽象出來的。如果您檢查 Next.js 9.5 之前產生的 <script> 標籤,您會注意到它們的 URL 遵循如下模式

/_next/static/ovgxWYrvKyjnlM15qtz7h/pages/about.js

上面的路徑區段 ovgxWYrvKyjnlM15qtz7h 是我們所謂的建置 ID。雖然這些檔案很容易在邊緣和使用者的機器上快取,但在重新建置您的應用程式後,建置 ID 會變更,並且所有快取都會失效。

對於大多數專案來說,這種權衡是可以接受的,但是,我們希望透過不再使未變更頁面的瀏覽器快取失效,來進一步最佳化此行為。

Next.js 9.2 中引入的改進的程式碼分割策略 是與 Google Chrome 團隊合作開發的,為 Next.js 頁面套件產生的這些改進奠定了一些基礎。

從 Next.js 9.5 開始,所有頁面 JavaScript 套件都將使用內容雜湊 (content hashes) 而不是建置 ID。這允許在部署之間未變更的頁面保留在瀏覽器和邊緣快取中,而無需再次下載。

相比之下,這些變更後的 URL 模式如下所示

#pages/about.qzfS4o5gIEXRME6sTEahL.js

qzfS4o5gIEXRME6sTEahL 部分不是全域建置 ID,而是 about.js 套件的確定性雜湊,只要您網站該部分的程式碼沒有變更,它就會保持穩定。此外,它現在透過跨重新部署長期快取,透過 Next.js 自動為您設定的 Cache-Control: public,max-age=31536000,immutable 進行快取。

快速重新整理增強功能

我們在 Next.js 9.4 中引入了快速重新整理,這是一種新的熱重新載入體驗,可讓您即時回饋對 React 元件所做的編輯。

Next.js 9.5 進一步完善了我們的快速重新整理實作,並為您提供了成功所需的工具

  • 易於理解的錯誤:所有編譯和執行階段錯誤都已更新為僅顯示相關資訊,包括導致錯誤的任何程式碼的程式碼框架
  • 開發時提示以保持元件狀態:Next.js 現在為您提供有用的提示,以確保快速重新整理將在盡可能多的情況下保持您的元件狀態。Next.js 提供的每個提示都是完全可操作的,並附有前後範例!
  • 元件狀態重設時的警告:當 Next.js 在編輯檔案後無法保持元件狀態時,我們現在將列印詳細的警告。此警告將幫助您診斷專案為何必須重設元件狀態,讓您可以修正它並充分利用快速重新整理。
  • 新文件:我們新增了大量文件,說明什麼是快速重新整理、它的運作方式以及預期結果!該文件還將教您如何透過解釋其錯誤恢復的工作原理來更好地利用快速重新整理。
  • 使用者程式碼疑難排解指南:新文件還包括關於如何在開發中充分利用快速重新整理的常見疑難排解步驟和提示

生產環境 React 分析

React 在不久前引入了Profiler API,讓您可以追蹤 React 元件中的效能問題。雖然此功能在開發中自動運作,但它需要使用單獨版本的 ReactDOM 才能在生產環境中進行分析。

透過 Next.js 9.5,您現在可以使用 next build 中的 --profile 標記啟用 React 的生產環境分析

next build --profile

之後,您可以像在開發中一樣使用分析器。

若要深入了解 React 分析,您可以閱讀 React 團隊關於 React Profiler 的文章。特別感謝 TODOrTotev@darshkpatel 貢獻此功能。

可選的全部捕捉路由

Next.js 9.2 版本新增了支援萬用字元動態路由 (catch-all dynamic routes),這項功能已被社群廣泛採用於各種使用情境。萬用字元路由讓您能彈性地建立高度動態的路由結構,並可由 Headless CMS、GraphQL API、檔案系統等技術驅動。

在聽取使用者回饋後,我們了解到使用者希望在比對路由最根層級時,能有更高的彈性。今天,我們很開心地為各位介紹選用性萬用字元動態路由 (optional catch-all dynamic routes),以滿足這些進階情境的需求。

若要建立選用性萬用字元路由,您可以使用 [[...slug]] 語法來建立頁面。

舉例來說,pages/blog/[[...slug]].js 將會比對 /blog,以及其下的任何路由,例如:/blog/a/blog/a/b/c 等等。

如同萬用字元路由,slug 會在路由器查詢物件 (router query object)中以路徑片段陣列的形式提供。因此,對於路徑 /blog/foo/bar 而言,查詢物件會是 { slug: ['foo', 'bar'] }。而對於路徑 /blog 而言,查詢物件將會省略 slug 鍵:{ }

您可以在我們的文件中深入了解選用性萬用字元路由

Webpack 5 支援 (beta 測試版)

Webpack 5 目前為 beta 測試版。它包含了一些重大的改進

今天我們很興奮地宣布 Next.js 正式推出 Webpack 5 beta 測試版。

若要試用 Webpack 5,您可以在您的 package.json 中使用 Yarn resolutions

package.json
{
  "resolutions": {
    "webpack": "^5.0.0-beta.30"
  }
}

Webpack 5 beta 測試版已在 nextjs.orgvercel.com 生產環境中推出。我們鼓勵您逐步試用,並在 GitHub 上回報您的發現。

編譯基礎架構的改進

為了支援 Webpack 5,我們重寫了許多編譯流程,使其更符合 Next.js 的需求

  • Next.js 不再依賴 webpack-hot-middlewarewebpack-dev-middleware,取而代之的是,我們現在直接使用 Webpack,並針對 Next.js 專案進行最佳化。這轉化為更簡潔的架構和更快的開發編譯速度。
  • 隨需載入 (On-demand-entries),也就是 Next.js 用於在您於開發期間訪問的頁面上進行編譯的系統,也已經過重寫,並且透過利用專為我們的使用情境量身打造的全新 Webpack 行為,現在變得更加可靠。
  • React Fast Refresh 和 Next.js 錯誤覆蓋層 (Error Overlay) 現在已完全相容於 Webpack 5
  • 磁碟快取 (Disk caching) 將在未來的 beta 測試版發布時啟用。

回溯相容性 (Backwards compatibility)

我們一直致力於確保 Next.js 與先前的版本回溯相容。

Webpack 4 將繼續獲得完整支援。我們正與 Webpack 團隊密切合作,以確保從 Webpack 4 遷移到 5 的過程盡可能順利。

如果您的 Next.js 專案沒有自訂 Webpack 設定,則無需進行任何專案變更即可完整利用 Webpack 5。

重要事項:如果您的專案有自訂 Webpack 設定,則可能需要進行一些變更才能轉換到 Webpack 5。我們建議您密切關注我們的遷移說明,或盡可能減少使用 Webpack 擴充功能,以實現未來無縫升級。

macOS 上改良的檔案監看功能 (Improved file watching on macOS)

我們最近發現 Webpack 的一個問題,在 macOS 上,對程式碼進行幾次變更後,檔案監看功能會停止運作。您必須手動重新啟動專案才能再次看到更新。在進行幾次變更後,這個循環會再次重複。

此外,我們發現這個問題不僅發生在 Next.js 專案中,也發生在所有基於 Webpack 建構的專案和框架中。

在花費數天時間偵錯這個問題後,我們將根本原因追溯到 Webpack 使用的檔案監看實作 chokidar,這是一個廣泛用於 Node.js 生態系統中的檔案監看實作。

我們向 chokidar 送出了一個修補程式 (patch)來修正此問題。在修補程式發布後,我們與 Tobias Koppers 合作,在新的 Webpack 版本中推出此修補程式。

當您升級到 Next.js 9.5 時,將會自動使用此已修補的 Webpack 版本。

結論

我們很高興看到 Next.js 的採用持續成長

  • 我們已經有超過 1,200 位獨立貢獻者**,**自 9.4 版本發布以來,新增了超過 135 位貢獻者。
  • 在 GitHub 上,這個專案已經獲得超過 51,100 次星號。

加入 Next.js 社群,請前往 GitHub Discussions。Discussions 是一個社群空間,讓您可以與其他 Next.js 使用者交流互動,並自由提問或分享您的作品。

例如,您可以從與大家分享您的專案網址開始。

如果您想回饋社群,但不確定如何著手,我們鼓勵您試用實驗性功能,例如我們的 Webpack 支援,並回報您的發現!

致謝 (Credits)

我們感謝我們的社群,包括所有幫助塑造此版本的外部回饋和貢獻。

特別感謝 Jan Potoms,一位長期的 Next.js 社群成員,他為此版本的多項功能做出了貢獻。

特別感謝 Tobias Koppers,Webpack 的作者,他協助 Next.js 實現 Webpack 5 支援。

本次發布歸功於以下貢獻者:@chandan-reddy-k、@Timer、@aralroca、@artemisart、@sospedra、@prateekbh、@Prioe、@Janpot、@merceyz、@ijjk、@PavelK27、@marbiano、@MichelleLucero、@thorsten-stripe、@TODOrTotev、@Skn0tt、@lfades、@timneutkens、@akhila-ariyachandra、@chibicode、@rafaelalmeidatk、@kirill-konshin、@jamesvidler、@JeffersonBledsoe、@tylev、@jamesmosier、@filipemarins、@Remeic、@vvo、@timothyis、@jazibsawar、@coetry、@adam-zacharski、@danwilliams、@tywmick、@matamatanot、@goldins、@mvllow、@its-tayo、@sshyam-gupta、@wilbert-abreu、@sebastianbenz、@jaydenseric、@developit、@dylanjha、@darshkpatel、@spinks、@stefanprobst、@moh12594、@jasonmerino、@cristiand391、@HyunSangHan、@mcsdevv、@M1ck0、@hydRAnger、@alexej-d、@valmassoi、@motleydev、@eKhattak、@jpedroschmitz、@JerryGoyal、@bowen31337、@phillip055、@balazsorban44、@chuabingquan、@youhosi、@andresz1、@bell-steven、@areai51、@Wssn、@ndom91、@anthonyshort、@zxzl、@jbowes、@IamLizu、@PascalPixel、@ralphilius、@ysun62、@muslax、@elsigh、@AsherFoster、@botv、@tomdohnal、@christianalfoni、@tomasztunik、@gsimone、@illuminist、@jplew、@OskarKaminski、@RickyAbell、@steph-query、@ericgoe、@MalvinJay、@cristianbote、@Ashikpaul、@jensmeindertsma、@amorriscode、@abhik-b、@awareness481、@LukasPolak、@arvigeus、@romMidnight、@jackyef、@drumm2k、@kuldeepkeshwar、@bogy0、@Belco90、@wawjr3d、@tanmaylaud、@SarKurd、@kevinsproles、@dstotijn、@styfle、@blackwright、@BrunoBernardino、@heyAyushh、@Necmttn、@TrySound、@obedparla、@NyashaNziramasanga、@tonyspiro、@kukicado、@ceorourke、@MehediH、@robintom、@karlhorky 和 @tcK1!