為什么 Next.js 不用 Vite 而要自造輪子 Turbopack?
Next.js 的 Github issues 中有一個帖子,反饋了 Next.js 的開發模式編譯很慢[1],自 2023 年 4 月 23 日提問以來,現在已經有 468 多條討論!看來這個問題不只一個人遇到,做為一個使用過 Next.js 的用戶來說,Next.js 的其它方面還可以,但是開發體驗真是挺糟糕的...
用戶 @roonie007 提出了自己的疑問,為什么 Next.js 不使用 Vite,而要重新發明輪子?[2]
做為 Next.js 和 Turbopack @vercel 的技術主管 @timneutkens[3] 對此進行了回復,如下所示:
我會盡量簡短,因為我可以寫/談論這個話題幾個小時 ??
幾年前,在 Vite 得到廣泛應用之前,我們開始看到越來越多基于 Next.js 構建的大型 Web 應用程序,包括企業級團隊中超過 100 名開發>人員的采用。這些代碼庫擴展到數萬個自定義組件,并且還從 npm 導入包。簡言之,盡管我們當時使用的 webpack(如果沒有選擇 Turbopack)實際上相當快速,但對于這些不斷增長的代碼庫規模來說仍然不夠快。
我們還看到了一種趨勢,即通用應用程序變得更加依賴編譯,主要是由組件庫/圖標庫的興起所致。今天,正如您在這個帖子中看到的,由于已發布的設計系統和圖標庫的使用,即使是一個非常小的應用程序也可能編譯出 20K 個或更多模塊。
我們發現的問題是,即使我們將 webpack 優化到最大,它仍然存在可以處理的模塊數量上限,因為如果你有 20,000 個模塊,即使每個模塊只花費 1 毫秒,也會導致 20 秒的處理時間,如果不能并行處理的話。
除此之外,我們不僅運行一個 webpack 編譯器,而是運行了三個:一個用于服務器,一個用于瀏覽器,一個用于邊緣運行時。這會帶來復雜性,因為這些獨立的編譯器必須協調工作,因為它們沒有共享的模塊圖。
在同一時間段,我們還開始探索 React Server Components、App Router,以及我們希望未來 5-10 年內 Next.js 開發應該是什么樣子的總體情況。其中一個主要議題是關于代碼如何從服務器->客戶端->服務器->客戶端,簡言之,如果您熟悉的話,就是服務器操作,特別是服務器操作可以返回包含額外客戶端組件的 JSX。為了使其工作,我們發現擁有一個統一的模塊圖,可以在同一個打包器/編譯器中同時容納服務器、客戶端和邊緣代碼,將會非常有益。這是像 Parcel 這樣的打包器長期探索的內容。
那時候,我們評估了所有現有的解決方案,并發現它們各自都有權衡之處,我不會像“拋棄其他人”一樣說它們,因為這些權衡都是有意義的,只是對于像 Next.js 這樣的框架,尤其是未來的 Next.js(如果我記得正確的話,這大概是在 ~2020 年左右)來說,它們不合適。
總體上來說,讓我們談一談一些目標,其中一些對用戶有益,一些對維護有益:
更快的 HMR
- Webpack 在模塊圖中的模塊數量上有性能限制。一旦達到 30K 模塊,每次代碼變更的開銷至少需要 ~1 秒的處理時間,無論您是進行小的 CSS 更改還是其他更改。
更快的路由初始編譯
- 具有 20-30K 模塊的 Webpack 通常需要 15-30 秒的處理時間,因為它無法跨 CPU 進行并行處理。
無破壞性變更
- 我們希望將所有這些改進帶給現有應用程序。作為其中的一部分,有很多 Next.js 特定的編譯器功能,如 next/font,需要添加進來。
可伸縮到最大的 TS/JS 代碼庫
- 如上所述,我們看到越來越大的代碼庫,為了優化這些,需要一種不同的架構。我認為在其他打包器中我們采用的架構最接近的是 Parcel。
持久性緩存
- Turbopack 擁有一個廣泛的緩存機制,可以與 Facebook 的 Metro 打包器(用于 react-native 和 instagram.com)相媲美,它能夠持久地緩存之前完成的工作,因此當您重新啟動開發服務器時,它只需恢復上次會話的緩存。目前正在積極地開發中。
與開發密切匹配的生產構建
- 目前在 Next.js 中,以及其他打包器中,dev/prod 之間存在差異,我們希望盡量減少這些差異。
超越當前打包器的生產優化
- 我們一直在開發先進的搖樹功能,允許在導入/導出級別而不是模塊級別進行代碼拆分,受到 Closure Compiler 的啟發。目前的打包器操作是在模塊級別上進行的。
減少編譯器/編譯時間中的不穩定性
- 目前由于服務器/客戶端/邊緣 webpack 編譯器之間的協調,有時會導致編譯時間較長。主要目標之一是減少實現的復雜性,并在一個編譯通道中輸出所有必需的文件。
(以后)Next.js 感知的打包器工具
- 比如大大改進的 bundle 分析,了解布局/頁面/路由情況。
(以后)Next.js / RSC 感知的打包優化
- 例如,優化客戶端組件以盡可能高效地捆綁。
維護者的完整可觀察性
- Next.js 的使用很廣泛,因此會產生大量的 bug 報告和功能請求。其中一種特別難以調查的 bug 報告與減速相關(這個問題就是一個很好的例子),以及內存使用("Next.js 泄漏內存"報告)。這些問題難以調查,因為它們需要深入了解報告者的性能分析/內存轉儲,而他們通常不愿意共享可運行的代碼。
這是為什么構建我們自己的工具對于我們是有益的一個重要原因,它使我們能夠調查報告的問題,而無需訪問您的代碼庫。如果我們使用其他任何打包器,我們將不得不說"很遺憾,這是您的問題,嘗試將其報告給該打包器的 GitHub 存儲庫",這不是我們想要做的事情,也不是我們之前在 webpack 中做過的。
就我個人而言,我很高興看到 Vite 在生態系統中做得很好。他們也從其他打包器中吸取了經驗教訓。如果您看看他們最近在 Rolldown 上的工作,您會發現有很多相似之處,這回歸到打包而不是“解包”以提高編譯性能,例如。
猜測寫這篇文章花了比我想要花的時間還要多,但希望對您有所幫助!
TLDR:其他打包器很棒,但它們不適合像 Next.js 這樣的框架。我們希望將這些改進帶給現有用戶,為此我們不得不構建一個新的打包器,吸取了之前嘗試過的許多不同方法的經驗教訓。
感興趣的可以通過以下參考資料閱讀原帖子。對此你怎么看?歡迎評論區討論!
參考資料
[1]Next.js 的開發模式編譯很慢: https://github.com/vercel/next.js/issues/48748
[2]為什么 Next.js 不使用 Vite,而要重新發明輪子?: https://github.com/vercel/next.js/issues/48748#issuecomment-2151880231
[3]技術主管 @timneutkens: https://github.com/vercel/next.js/issues/48748#issuecomment-2199941311