編輯評論
Railway 這次的前端架構重構,是近年來罕見的大規模框架遷移案例,特別值得關注的是其遷移方式:僅用兩個 PR 就完成 200+ 路由的零停機遷移。這背後反映的技術決策,對於所有正在評估 Next.js 是否適合自己項目的團隊都有啟發意義。
從技術角度來看,這次遷移的核心問題是「框架選擇應該基於實際需求,而非流行趨勢」。Next.js 雖然是目前最受歡迎的 React 框架,但其設計哲學是「伺服器優先」(server-first),而 Railway 的產品特性是「客戶端優先」(client-first)—— 儀表板是狀態豐富的介面、畫布是即時互動的、WebSockets 無處不在。當你的應用程式特性與框架設計哲學不匹配時,構建時間達到 10 分鐘就不意外了。
TanStack Start + Vite 的選擇展示了一種新趨勢:開發者開始重新審視「開發體驗」與「生產需求」的平衡。Vite 的啟動速度和 HMR 性能已經成為許多團隊的硬性需求,而 TanStack Router 提供的型別安全路由和一級公民的 layout 支援,正是 Next.js Pages Router 長期欠缺的。對於需要快速迭代的前端團隊來說,這次遷移帶來的效能提升不僅是構建時間的縮短,更是開發者體驗的質變。
結論摘要
- Railway 將生產環境前端從 Next.js 全面遷移至 TanStack Start + Vite,涵蓋儀表板、畫布和 railway.com 全站
- 構建時間從 10 分鐘以上縮減至 2 分鐘以下,其中 Next.js 原本佔用 6 分鐘
- 遷移僅用兩個 PR 完成,處理 200+ 路由,實現零停機部署
- 核心原因是 Next.js 的伺服器優先設計與 Railway 客戶端優先的產品特性不匹配
- Railway 使用自己的雲端平台執行前端,展示預覽部署、健康檢查和零停機部署能力
原文翻譯
Railway 前端大遷移:捨棄 Next.js,改用 TanStack Start + Vite
Railway 的整個生產環境前端不再執行於 Next.js。儀表板、畫布、railway.com,全部現在都執行於 Vite + TanStack Router,我們在兩個 PR 中完成了遷移,實現了零停機。
Next.js 曾經很好用,後來不再適合
Next.js 讓 railway.com 從零發展成每月服務數百萬用戶的生產應用。它是一個優秀的框架,但它不再是適合我們產品的選擇。
前端構建時間已經爬升到超過 10 分鐘。其中 6 分鐘是 Next.js 造成的,一半時間卡在「完成頁面優化」。對於一個每天多次發布的團隊來說,這種構建時間不是小問題,而是對每次迭代徵收的高昂稅款。
Railway 的應用程式絕大部分是客戶端導向的。儀表板是一個豐富、有狀態的介面。畫布是即時的。WebSockets 無處不在。Next.js 的伺服器優先原語不是我們使用的東西,我們最終在 Pages Router 上建立了自己的抽象層,只為了支援框架沒有以我們需要的方式處理的 layout 和路由關注點。
我們仍然使用 Pages Router,這使得共享 layout 變得 hacky。每個 layout 模式都是一個附加的權宜之計,而不是一級框架原語。App Router 本來可以解決其中一些問題,但它嚴重偏向伺服器優先模式,而我們的產品是有意設計為客戶端驅動的。採用它意味著圍繞一個我們不需要的範式重建。
為什麼選擇 TanStack Start + Vite
我們想要一個符合我們實際構建方式的技術堆疊:明確、客戶端優先、快速迭代。我們也確實喜歡使用它。
對於產品團隊,我們想要一些幫助我們避免思考如何實現前端的便利功能,發現以下功能真的說服了我們:
- 開箱即用的型別安全路由。路由參數和搜尋參數是推斷的,自動完成在整個路由樹中工作,路由本身從檔案系統生成。
- 一級公民的 layout。無路徑 layout 路由用可組合和可預測的東西替換了我們所有的先前 hack。
- 快到讓你不再思考它的開發迴圈。即時 HMR,近乎零的啟動時間。更改代碼和看到結果之間的回饋循環有效地消失了。
- 在真正重要的地方使用 SSR。行銷頁面、更新日誌、職涯。其他地方純客戶端,因為我們不會在無法受益的畫面上強制伺服器渲染。(編輯:諷刺的是,這個 Blog 還沒有在 Tanstack 上。)
- 明確的模型。意味著,我們發現 TanStack 較少依賴框架魔法,對底層實際工作方式有更多控制。
我們中的幾個人在假期嘗試了 TanStack Start,反應是一致的。我們喜歡用它構建,對於像 Railway 儀表板這樣的產品,這與任何基準測試一樣重要。
兩個 PR,零停機
一旦做出選擇,我就開始工作。在合併前壓縮之前,我一定做了數百次提交。
遷移一個服務數百萬用戶、跨越 200+ 路由的生產前端,通常是需要數月並行運行和增量切換的事情。我們有截止日期,所以我用兩個 PR 做到了。
PR 1 替換了所有 Next.js 特定的東西:next/image、next/head、next/router。每個都被替換為原生瀏覽器 API 或框架無關的替代品。這個 PR 沒有改變框架本身的任何東西。它只是移除了對它的所有依賴,以便 PR 2 可以是一個乾淨的交換。
PR 2 交換了框架。200+ 路由遷移。我們首先系統性地將所有非路由相關的東西從頁面檔案中提取到獨立的 React 元件中,然後從原始頁面樹生成所有路由。
然後我們添加 Nitro 作為伺服器層,用 Nitro 配置替換 next.config.js,將重定向(500+)、安全標頭和快取規則合併到一個地方。我們還用瀏覽器原生替代品替換了 Next.js 提供了 polyfill 的 Node.js API(Buffer、url.parse 等),這作為副作用留給了我們更乾淨的代碼。
在一個週日清晨合併。團隊立即在 Discord 的一個即時戰況室中進行 dogfooding,同一天落地了一系列修復。無停機。
我們放棄了什麼
當然我們獲得了一個更快、更明確的堆疊,但不是沒有權衡。
- 內建圖像優化。我們用
<img>標記和 Fastly 邊緣圖像優化替換了next/image。 - 生態系統的部分。我們用小型內部等效物替換了
next-seo和next-sitemap等工具。構建簡單,沒有額外依賴。 - 成熟度。TanStack Start 是新的,可以預期更粗糙的邊緣。我們對此感到舒服,因為方向是正確的,維護者有回應,我們資助 Vite 和 TanStack 是因為我們相信他們的方向。
Railway 的前端在 Railway 上執行
我們以與用戶執行前端相同的方式執行我們的生產前端:每個 PR 的預覽部署、健康檢查、零停機推出。當我們交換整個構建系統和框架時,我們沒有觸摸基礎設施。我們更改代碼,推送它,Railway 處理其餘部分。
Fastly 現在直接從邊緣提供我們的大部分流量。行銷頁面被快取,動態頁面在需要的地方使用 ISR,結果我們的前端伺服器大多數是空閒的。Vite 的資產模型使這特別有效。每個模組都有自己的內容雜湊塊,因此推出計費更改只會使該塊無效。返回用戶下載千字節,而不是兆字節。
這就是我們認為前端應該部署的方式:構建快,資產不可變且對快取友好,底層基礎設施處理推出、預覽和路由,而不需要你思考它。你的前端框架應該針對迭代速度優化,你的基礎設施應該讓推出那些迭代不可見。這就是我們為自己以及 Railway 上的每個人建立的體驗。
為什麼是現在
前端的迭代速度現在比以往任何時候都重要。
以前需要 10 多分鐘的構建現在不到 2 分鐘完成。開發伺服器即時啟動。路由更改在邊界進行型別檢查。Layout 無需權宜之計即可組合。
編寫代碼和將其放在用戶面前的差距是瓶頸,我們在這裡做的一切,框架交換、邊緣快取、資產模型,都是關於關閉這個差距。Vite + TanStack 為我們設置了一個推出前端更改近乎即時的世界,這就是我們正在建構的世界。