2019 年 7 月 8 日 星期一
Next.js 9
發布者經過 70 個 Canary 版本發布後,我們很高興推出 Next.js 9,其特色包括
- 內建零配置 TypeScript 支援:透過自動 TypeScript 支援和整合的類型檢查,更有信心地建置您的應用程式。
- 基於檔案系統的動態路由:透過檔案系統表達複雜的應用程式路由需求,無需自訂伺服器。
- 自動靜態最佳化:預設情況下,利用伺服器端渲染和靜態預先渲染建立超快速網站,而不會犧牲功能。
- API 路由:快速建置後端應用程式端點,利用熱重載和統一的建置管線。
- 更多生產環境最佳化:由於視窗內預先載入和其他最佳化,應用程式比以往更具響應性。
- 改善的 DX(開發者體驗):不唐突、易於使用的改進,可協助您發揮最佳開發效能。
一如既往,我們努力確保所有這些優勢都向後相容。對於大多數 Next.js 應用程式,您只需執行
npm i next@latest react@latest react-dom@latest
在極少數情況下,您的程式碼庫可能需要變更。請參閱升級指南以取得更多資訊。
自上次發布以來,我們很高興看到像 IGN、Bang & Olufsen、Intercom、Buffer 和 Ferrari 等公司使用 Next.js 發布產品。查看範例展示以了解更多資訊!
內建零配置 TypeScript 支援
一年前,Next.js 6 透過名為 @zeit/next-typescript
的外掛程式引入了基本的 TypeScript 支援。使用者還必須自訂他們的 .babelrc
並在 next.config.js
中啟用它。
設定完成後,此外掛程式將允許 Next.js 建置 .ts
和 .tsx
檔案。但是,它沒有整合類型檢查,Next.js 核心也沒有提供類型。這表示必須在 DefinitelyTyped 中單獨維護社群套件,而該套件可能與發布版本不同步。
在與許多現有和新的使用者交談時,很明顯大多數人對使用 TypeScript 非常感興趣。他們想要更可靠和標準的解決方案,以便輕鬆地將 TypeScript 整合到他們現有或新的程式碼庫中。
因此,我們著手將 TypeScript 支援整合到 Next.js 核心中,改善開發者體驗,並在此過程中使其速度更快。
自動設定
在 Next.js 中開始使用 TypeScript 很簡單:將任何檔案、頁面或組件從 .js
重新命名為 .tsx
。然後,執行 next dev
!
這將使 Next.js 偵測到您的專案中正在使用 TypeScript。Next.js CLI 將引導您完成安裝 React 和 Node.js 的必要類型。
如果預設的 tsconfig.json
檔案尚不存在,Next.js 也會建立一個具有合理預設值的檔案。此檔案允許在 Visual Studio Code 等編輯器中進行整合的類型檢查。
整合的類型檢查
Next.js 會在開發和建置生產環境版本時為您處理類型檢查。
在開發期間,Next.js 會在您儲存檔案後顯示類型錯誤。類型檢查在背景中進行,讓您可以立即在瀏覽器中與更新後的應用程式互動。類型錯誤會在可用時傳播到瀏覽器。
如果存在類型錯誤,Next.js 也會自動使生產環境建置(即 next build
)失敗。這有助於防止將損壞的程式碼發布到生產環境。

Next.js 核心以 TypeScript 編寫
在過去的幾個月中,我們已將大部分程式碼庫遷移到 TypeScript,這不僅加強了我們的程式碼品質,還讓我們能夠為所有核心模組提供類型。
例如,當您匯入 next/link
時,支援 TypeScript 的編輯器將顯示允許的屬性以及它們接受的值。

動態路由片段
動態路由(也稱為 URL Slug 或美觀/簡潔 URL)是 Next.js 在 2.5 年前發布後,GitHub 上最早的功能請求之一!
此問題在 Next.js 2.0 中透過引入自訂伺服器 API 以程式化的方式使用 Next.js 而「解決」。這允許使用 Next.js 作為渲染引擎,從而實現抽象化和傳入 URL 的映射以渲染特定頁面。
我們與使用者交談並檢查了他們的許多應用程式,發現他們中的許多人都有自訂伺服器。出現了一種模式:自訂伺服器最主要的原因是動態路由。
但是,自訂伺服器有其自身的缺點:路由在伺服器層級而不是代理伺服器層級處理,它以單體式架構部署和擴展,並且容易出現效能問題。
由於自訂伺服器需要整個應用程式在一個執行個體中可用,因此通常難以部署到解決這些問題的無伺服器環境。無伺服器請求在代理層級路由,並獨立擴展/執行,以避免效能瓶頸。
此外,我們相信我們可以提供更好的開發者體驗!當您建立名為 pages/blog.js
的檔案,並且突然有一個可透過 /blog
存取的頁面時,Next.js 的許多魔力就開始了。
為什麼使用者需要建立自己的伺服器並了解 Next.js 的程式化 API,才能支援像 /blog/my-first-post
(/blog/:id
) 這樣的路由?
基於此回饋和願景,我們開始研究路由映射解決方案,其驅動力來自使用者已經知道的東西:pages/
目錄。
建立動態路由頁面
Next.js 支援使用基本具名參數建立路由,這種模式由 path-to-regexp
(為 Express 提供支援的程式庫)普及化。
現在,透過在您的 pages
目錄中建立一個名為 pages/post/[pid].js
的檔案,即可建立符合 /post/:pid
路由的頁面!
Next.js 將自動比對像 /post/1
、/post/hello-nextjs
等請求,並渲染在 pages/post/[pid].js
中定義的頁面。比對的 URL 片段將作為查詢參數傳遞到您的頁面,名稱在 [方括號]
之間指定。
例如:給定以下頁面和請求 /post/hello-nextjs
,query
物件將為 { pid: 'hello-nextjs' }
static async getInitialProps({ query }) {
// pid = 'hello-nextjs'
const { pid } = query
const postContent = await fetch(
`https://api.example.com/post/${encodeURIComponent(pid)}`
).then(r => r.text())
return { postContent }
}
也支援多個動態 URL 片段!
目錄名稱和檔案名稱都支援 [param]
語法,這表示以下範例都有效
./pages/blog/[blogId]/comments/[commentId].js
./pages/posts/[pid]/index.js
您可以在 Next.js 文件或 Next.js 學習章節中閱讀有關此功能的更多資訊。
自動靜態最佳化
Next.js 在大約兩年前發布的 v3 中新增了對靜態網站產生的支援。當時,這是要求新增到 Next.js 的最受歡迎的功能。
這是理所當然的:不可否認的是,靜態網站速度很快! 它們不需要伺服器端運算,並且可以從 CDN 位置立即串流到終端使用者。
但是,伺服器端渲染或靜態產生應用程式之間的選擇是二元的,您要么選擇伺服器端渲染,要么選擇靜態產生。沒有中間地帶。
實際上,應用程式可能有不同的需求。這些需求需要不同的渲染策略和權衡。
例如,首頁和行銷頁面通常包含靜態內容,非常適合靜態最佳化。
另一方面,產品儀表板可能會受益於伺服器端渲染,因為資料會頻繁更新。
我們開始探索如何為使用者提供兩全其美的方法,並預設情況下速度更快。我們如何為使用者提供靜態行銷頁面和動態伺服器渲染頁面?
從 Next.js 9 開始,使用者不再需要在完全伺服器渲染或靜態匯出其應用程式之間做出選擇。在每個頁面的基礎上為您提供兩全其美的方法。
自動部分靜態匯出
引入了啟發式方法,以自動判斷頁面是否可以預先渲染為靜態 HTML。
此判斷是根據頁面是否透過使用 getInitialProps
來判斷頁面是否具有封鎖資料需求。
此啟發式方法允許 Next.js 發出包含伺服器渲染和靜態產生頁面的混合應用程式。
內建的 Next.js 伺服器 (next start
) 和程式化 API (app.getRequestHandler()
) 都透明地支援此建置輸出。無需設定或特殊處理。
靜態產生的頁面仍然是反應式的:Next.js 將在用戶端注水您的應用程式,使其具有完整的互動性。
此外,如果頁面依賴 URL 中的查詢參數,Next.js 將在注水後更新您的應用程式。
如果頁面將在開發期間靜態產生,Next.js 將以視覺化方式通知您。可以透過點擊隱藏此視覺化產物。

靜態產生的頁面也將顯示在 Next.js 的建置輸出中

API 路由
在許多情況下,當建置 React 應用程式時,您最終需要某種後端。要么從資料庫檢索資料,要么處理使用者提供的資料(例如聯絡表單)。
我們發現,許多需要後端的使用者使用自訂伺服器建置了他們的 API。這樣做時,他們遇到了相當多的問題。例如,Next.js 不會編譯自訂伺服器程式碼,這表示您無法使用 import
/export
或 TypeScript。
因此,許多使用者最終在自訂伺服器之上實作了自己的自訂編譯管線。雖然這解決了他們的目的,但它容易出現許多缺點:例如,當設定不正確時,樹狀搖晃將為他們的整個應用程式停用。
這引發了一個問題:如果我們將 Next.js 提供的開發者體驗帶到建置 API 後端,會怎麼樣?
今天,我們很高興推出 API 路由,這是來自 Next.js 的一流開發者體驗,用於建置您的後端。
若要開始使用 API 路由,請在 pages/
目錄中建立一個名為 api/
的目錄。
此目錄中的任何檔案都將自動映射到 /api/<您的路由>
,方式與其他頁面檔案映射到路由的方式相同。
例如,pages/api/contact.js
將映射到 /api/contact
。
注意:API 路由也支援動態路由!
pages/api/
目錄中的所有檔案都匯出請求處理函式,而不是 React 組件
export default function handle(req, res) {
res.end('Hello World');
}
req
指的是 NextApiRequest,它擴展了 http.IncomingMessageres
指的是 NextApiResponse,它擴展了 http.ServerResponse
一般來說,API 端點會接收一些傳入資料,例如查詢字串、請求主體或 Cookie,並以其他資料回應。
在研究將 API 路由支援新增到 Next.js 時,我們注意到在許多情況下,使用者沒有直接使用 Node.js 請求和回應物件。相反地,他們使用了伺服器程式庫(如 Express)提供的抽象化。
這樣做的原因是,在許多情況下,傳入資料是某種形式的文字,必須先解析才能有用。因此,這些特定的伺服器程式庫有助於消除手動解析資料的負擔,最常見的是透過中介層。最常用的中介層提供查詢字串、主體和 Cookie 解析,但它們仍然需要一些設定才能開始使用。
Next.js 中的 API 路由將預設提供這些中介層,以便您可以立即高效地建立 API 端點
export default function handle(req, res) {
console.log(req.body); // The request body
console.log(req.query); // The url querystring
console.log(req.cookies); // The passed cookies
res.end('Hello World');
}
除了使用傳入資料外,您的 API 端點通常也會傳回資料。通常,此回應將是 JSON。Next.js 預設提供 res.json()
,使傳送資料更容易
export default function handle(req, res) {
res.json({ title: 'Hello World' });
}
在開發中變更 API 端點時,程式碼會自動重新載入,因此無需重新啟動伺服器。
生產環境最佳化
預先載入視窗內 <Link>
Next.js 9 將在 <Link>
組件出現在視窗內時自動預先載入它們。
此功能透過加快導航到新頁面的速度來提高應用程式的響應速度。
Next.js 使用 Intersection Observer 來預先載入背景中必要的資源。
這些請求具有低優先級,並讓位於 fetch()
或 XHR 請求。如果使用者啟用了節省流量模式,Next.js 將避免自動預先載入。
您可以透過將 prefetch
屬性設定為 false
來選擇退出此功能,以用於很少訪問的頁面
<Link href="/terms" prefetch={false}>
<a>Terms of Service</a>
</Link>
預設最佳化 AMP
Next.js 9 現在預設為 AMP 優先和混合 AMP 頁面渲染最佳化 AMP。
雖然 AMP 頁面是選擇加入的,但 Next.js 將自動最佳化其輸出。這些最佳化可以將渲染速度提高多達 50%!
此變更是由於 Sebastian Benz 在 AMP Optimizer 上的出色工作才得以實現。
消除 typeof window
分支的無效程式碼
Next.js 9 在伺服器和用戶端建置期間,將 typeof window
替換為其適當的值(undefined
或 object
)。此變更允許 Next.js 自動從您的生產環境建置的應用程式中移除無效程式碼。
使用者如果應用程式的 getInitialProps
或其他部分有僅限伺服器端程式碼,應該會看到他們的用戶端套件大小減少。
開發者體驗改進
編譯指示器
在 9 之前的版本中,要知道熱代碼替換即將發生(以及 Next.js 編譯器工具鏈正在工作)的唯一方法是查看開發者主控台。
然而,許多時候人們都在看最終的渲染結果,這使得很難知道 Next.js 是否仍在進行編譯工作。例如,您可能正在對頁面上的樣式進行細微的更改,而您不會立即知道它們是否已更新。
因此,我們建立了一個RFC /「好的入門議題」,以討論指示工作正在進行的問題的潛在解決方案。
我們收到了許多設計師和工程師對 RFC 的回饋,例如他們偏好的內容以及指示器設計的潛在方向。
Rafael Almeida 藉此機會與我們的團隊合作,實作了一個全新的指示器,現在預設在 Next.js 9 中提供。
每當 Next.js 正在進行編譯工作時,您會在頁面右下角看到一個小三角形出現!
主控台輸出
傳統上,在開發中進行更改時,Next.js 會顯示一個編譯指示器狀態,並帶有載入狀態條填滿,並且在您進行更改時會不斷清除螢幕。
此行為會導致一些問題。最值得注意的是,它會清除來自您的應用程式碼的主控台輸出,例如當您將 console.log
新增至您的元件時。而且當使用外部工具將日誌輸出縫合在一起時,例如 Vercel CLI 或 docker-compose
。
從 Next.js 9 開始,日誌輸出跳動較少,並且不再清除螢幕。這允許更好的整體體驗,因為您的終端機視窗將具有更多相關資訊,並且閃爍更少,同時 Next.js 將與您可能已經使用的工具更好地整合。
特別感謝 Justin Chase 在輸出清除方面進行協作。
建置輸出統計
使用 next build
為生產環境建置您的應用程式時,現在它將為您提供所有已建置頁面的詳細視圖。
每個頁面都會自動收到一些統計資訊。
最突出的一個是套件大小。隨著您的應用程式增長,您的 JavaScript 套件也會增長,此建置時指示將幫助您指示生產套件的增長。在未來,您還可以為頁面設定效能預算,如果超出預算,生產環境建置將會失敗。

除了套件大小,我們還顯示每個頁面中使用了多少專案元件和 node_modules
元件。這指示了頁面的複雜性。

每個頁面還會指示它是靜態最佳化還是伺服器端渲染,因為每個頁面的行為都可能不同。

每頁配置物件
現在每個頁面都可以匯出一個配置物件。最初,此配置允許您選擇加入AMP,但在未來,您將能夠配置更多頁面特定的選項。
export const config = { amp: true };
export default function AboutPage(props) {
return <h3>My AMP About Page!</h3>;
}
若要選擇加入混合 AMP 渲染,您可以使用值 'hybrid'
import { useAmp } from 'next/amp';
export const config = { amp: 'hybrid' };
export default function AboutPage(props) {
const isAmp = useAmp();
return <h3>My About Page!{isAmp ? <> Powered by AMP!</> : ''}</h3>;
}
withAmp
較高階元件已移除,以支持此新配置。
我們提供了一個 codemod,可自動將 withAmp
的用法轉換為新的配置物件。您可以在升級指南中閱讀更多相關資訊。
程式碼庫改進
我們最近對我們的工具進行了一些變更,以便在為程式碼庫做出貢獻時提供更好的體驗,並確保程式碼庫增長時的穩定性。
正如您在 TypeScript 章節下讀到的那樣,Next.js 核心現在以 TypeScript 編寫,並且自動為 Next.js 應用程式產生類型定義以供使用。除了這對使用 Next.js 建置的應用程式很有用之外,在處理核心程式碼庫時也很有用。因為您可以自動獲得類型錯誤和自動完成。
Next.js 已經有一個相當大的整合測試套件,其中包含 50 多個 Next.js 應用程式,並針對它們運行測試。這些測試確保在發布新版本時,升級是順暢的,因為之前可用的功能已針對相同的測試套件進行了測試。
我們的大多數測試都是整合測試,因為在許多情況下,它們複製了「真實」開發人員在開發中使用 Next.js 的情況。例如,我們有複製對 Next.js 應用程式進行更改以查看熱模組替換是否有效的測試。
我們的整合測試主要基於 Selenium webdriver,我們將其與 chromedriver 結合使用,以在無頭 Chrome 中進行測試。然而,隨著時間的推移,某些問題會在其他瀏覽器中出現,尤其是像 Internet Explorer 11 這樣的舊瀏覽器。
由於我們使用了 Selenium,我們能夠在多個瀏覽器上自動運行我們的測試。
截至目前,我們正在 Chrome、Firefox、Safari 和 Internet Explorer 11 上運行我們的測試套件。
Google Chrome 協作
Google Chrome 團隊一直致力於透過貢獻 RFC 和 pull request 來改進 Next.js。
此協作的目標是大規模效能改進,重點是套件大小、啟動和 hydration 時間。
例如,這些變更將改善小型網站的體驗,但也將改善像 Hulu、Twitch 和 Deliveroo 等大型應用程式的體驗。
Module / Nomodule
第一個重點領域是將現代 JavaScript 傳輸到支援現代 JavaScript 的瀏覽器。
例如,目前 Next.js 必須為 async
/await
語法提供 polyfill,因為程式碼可能會在不支援 async
/await
的瀏覽器中執行,這會導致崩潰。

為了避免在將現代 JavaScript 發送到支援它的瀏覽器的同時破壞舊版瀏覽器,Next.js 將使用 module/nomodule 模式。 module/nomodule 模式提供了一種可靠的機制,用於為現代瀏覽器提供現代 JavaScript,同時仍然允許舊版瀏覽器回退到 polyfill 的 ES5。
Next.js 中 module/nomodule 的 RFC 可以在此處找到。
改進的套件拆分
Next.js 中目前的套件拆分策略是基於一個基於比率的啟發式方法,用於將模組包含在單個「commons」chunk 中。由於只有一個套件,因此粒度非常小,程式碼要么不必要地被下載(因為 commons chunk 可能包含特定路由實際上不需要的程式碼),要么程式碼在多個頁面套件中重複。

改進的套件拆分的 RFC 可以在此處找到。
其他改進
Chrome 團隊還致力於許多其他最佳化和變更,這些將改進 Next.js。這些 RFC 將很快分享。
這些 RFC 和 pull request 被標記為「Collaboration」,以便可以在 Next.js issue tracker 中輕鬆找到它們。
社群
我們很高興看到 Next.js 社群的持續成長。
此版本有超過 65 位 pull request 作者貢獻了核心改進或範例。
談到範例,我們現在提供了超過 200 個範例,說明如何將 Next.js 與不同的函式庫和技術整合!包括大多數 css-in-js 和資料獲取函式庫。
- 我們已經有超過 720 位貢獻者 至少提交了 1 次 commit。
- 在 GitHub 上,該專案已被標註星號超過 38,600 次。
- 自首次發布以來,已提交超過 3,400 個 pull request,自上次主要版本以來已提交 超過 800 個!
自上次主要版本以來,Next.js 社群已成長了一倍,擁有超過 8,600 名成員。加入我們!
我們感謝我們的社群以及所有外部回饋和貢獻,這些幫助塑造了此版本。