React團隊迴應用Vite替換Create React App的建議

語言: CN / TW / HK

最近,國外網友 t3dotgg 建議 React 官方把文件中關於建議使用 Create React App 來建立新專案更換為建議使用 Vite 來建立新專案。多數網友對此表示贊同:

React 新的官方文件釋出在即(目前顯示已完成 99%),然而 Beta 版官方文件中仍然推薦使用 Create React App 建立新專案。另外提供了兩個備選方案:ViteParcel

檢視 Create React App 的 Github 倉庫可以發現,其已經 5 個月沒有更新了,積累了 1500+ 個 issues

1 月 31 日,React 團隊核心成員 Dan Abramov 對此建議進行了回覆,解釋了 React 團隊成員對此建議的權衡並提供了一些選項,下面就來看看詳細內容吧(可以跳到最後看結論)!


Create React App 的演變

在 2016 年釋出 Create React App 時,工具的環境是分散的。如果想要將 React 新增到現有應用,需要新增一個 <script> 標籤或從 npm 中匯入,然後調整現有的構建工具配置。但是,如果要從頭開始建立一個僅使用 React 構建的新應用,則沒有明確的方法可以做到這一點。

在 Create React App 之前,必須安裝一堆工具並將它們連線在一起,提供正確的預設以使用 JSX,為開發和生產環境進行不同的配置,為資源快取提供正確的設定,配置 linter 等,想要正確完成這一系列工作非常困難。人們通過建立和共享可以克隆的“樣板”儲存庫來解決了這個問題。然而,這產生了另外一個問題:一旦在專案中調整了克隆的樣板檔案,就很難再拉取樣板的更新。這樣,專案的設定會變得舊,要麼放棄更新,要麼花費大量精力讓所有工具再次協同工作。在快速發展的生態系統中,這非常困難。

Create React App 通過將多個工具組合在一個包中解決了這個問題。現在,如果想用 React 開始一個新專案,有一個明確的推薦方法(Create React App)可以做到這一點! 然後,每隔一段時間,可以更新這個包,以獲得所有底層工具的更新。這種模型變得很流行,以至於今天有很多工具都以這種方式工作。Vite 確實是擁有相似願景的最佳工具之一,並且在在某些方面更進一步。

Create React App 的目標是為大多數 React 使用者提供啟動新 React Web 應用的最佳方式,它支援一組協同工作的精選功能。隨著時間的推移,它提供的開箱即用的“baseline”會隨著我們找到正確的權衡而擴大。 例如,為執行時錯誤添加了一個遮罩層,添加了對不同樣式選項的支援,預設添加了快速重新整理,它允許儲存元件的程式碼並檢視更改而不會丟失狀態。對於預設的 React 開發體驗來說,這是一個巨大的里程碑。總的來說,由於 Create React App 完全控制了編譯管道,因此新增編譯相關的功能是很容易的。

有這樣一個精心策劃的設定對生態系統仍然很有價值。當 React Hooks 出現時,React 團隊將 React Hooks lint 規則新增到預設設定中。除此之外,Create React App 還允許 React 團隊向儘可能廣泛的受眾部署重要的工具更改(快速重新整理支援、React Hooks lint 規則)。 如果沒有 React 團隊策劃的流行模板,將很難如此廣泛地推出這些工具更改。

Create React App 的問題

隨著時間的推移,Create React App 停滯不前。許多人指出它比替代品慢,並且不支援人們如今想要使用的一些流行工具。原則上,React 團隊可以解決這些問題。例如,可以更新Create React App 內部,以使用更快的 bundler,甚至在內部使用 Vite。或者可以建議人們從 Create React App 遷移到 Vite 這樣的應用。然而,React 團隊還想解決一個更深層次的問題。

按照設計,Create React App 會生成一個純客戶端應用。這意味著用它建立的每個應用都包含一個空的 HTML 檔案、一個帶有 React 的 <script> 標籤和應用包。當載入空的 HTML 檔案時,瀏覽器會等待 React 程式碼和全部應用包下載。這在低頻寬連線上可能需要一段時間,並且此時使用者在螢幕上看不到任何內容。然後,載入應用程式碼。此時使用者會在螢幕上看到一些內容——但通常還需要載入資料。所以程式碼傳送了載入資料的請求,使用者需要等待它返回。 最後,資料載入,元件重新渲染資料,使用者看到最終結果。

這是非常低效的,儘管如果只在客戶端執行 React 很難做得更好。將其與 Rails 這樣的服務端框架進行對比:服務端將立即啟動資料獲取,然後生成包含所有資料的頁面。在這種情況下,使用者會看到包含所有資訊的 HTML 檔案,而不是等待載入指令碼的空白檔案。HTML 是Web 的基石——那麼為什麼建立React 應用會產生一個空的 HTML 檔案?為什麼不利用 Web 最基本的功能——在所有互動程式碼載入之前快速檢視內容的能力? 為什麼要等到所有客戶端程式碼載入完成後才開始載入資料?

Create React App 只解決了問題的一方面,它提供了良好的開發體驗,但它沒有強加足夠的結構來幫助我們利用 Web 的強大功能獲得良好的使用者體驗。開發者可以嘗試自己解決這些問題,但這違背了 Create React App 的宗旨。每個真正高效的 React 設定都是自定義的、不同的,並且是 Create React App 無法實現的。

這些使用者體驗問題並不是 Create React App 特有的。它們甚至不特定於 React。例如,從 Preact、Vue、Lit 和 Svelte 的 Vite 主頁模板建立的應用都會遇到相同的問題。這些問題是沒有靜態站點生成 (SSG) 或服務端渲染 (SSR) 的純客戶端應用所固有的。

React 框架的興起

有些人可能不喜歡完全使用 React 進行構建。例如,可以在服務端或在構建過程中使用不同的工具(如 Jekyll 或 Astro)生成 HTML 頁面。這解決了空 HTML 檔案的問題,但是必須混合使用兩種渲染現技術。隨著時間的推移,想要新增的互動性越多,這種技術割裂就越明顯。

這種割裂不僅會損害開發人員的體驗——使用者體驗也會受到影響。使用真正以 HTML 為中心且未充分利用 React 的工具,每個頁面導航都會變成完整的頁面重新載入,從而清除了所有客戶端狀態。如今,許多使用者希望在應用內導航順暢,而不是 90 年代風格的整頁重新載入。同樣,許多開發人員更喜歡使用單一的渲染模型而不是混合兩種不同的模型來構建他們的應用。開發者想用 React 構建整個應用。

如果使用 React 構建整個應用,那麼能夠使用 SSG/SSR 很重要。 在 Create React App 中缺乏對它們的支援。除此之外,經過多年的生態系統創新,React 的許多其他問題現在都有了成熟的解決方案。 例如,network waterfalls 和 bundle 大小。

即使應用沒有像面向內容的網站那樣從 SSG 或 SSR 中獲益,它也可能會受到網路瀑布的影響。如果在掛載時獲取資料,則在載入所有程式碼並渲染元件之前,第一次資料獲取甚至不會開始。這是一個 waterfall:如果應用知道如何在程式碼仍在載入時開始獲取資料,那麼就可以並行完成。在導航中,如果父元件和子元件都需要獲取某些內容,則會產生更糟糕的 waterfall。當我們談論 React 效能時,無法迴避一個事實:對於如此多的應用來說,waterfall 是效能的瓶頸。要解決這些 waterfall,需要將資料獲取與路由整合起來,而Create React App 無法做到這一點。

我們的應用程式碼會隨著新增的每個新功能和額外依賴項而不斷增長。如果經常部署,應用在每次使用時載入速度可能會變得非常慢,因為它總是需要載入所有程式碼。有幾種方法可以解決這個問題;可以移動一些程式碼以在服務端或在構建期間執行(如果工具允許)。理想情況下,還可以按路由拆分程式碼。然而,如果嘗試手動進行程式碼拆分,通常會使效能更差。要解決這一問題,需要將資料獲取與路由和打包相結合,而 Create React App 無法做到這一點。

React 本身只是一個庫。它不規定如何使用路由或資料獲取。 Create React App 也沒有。不幸的是,這意味著單靠 React 和最初設計的 Create React App 都無法解決這些問題。服務端渲染和靜態生成、資料獲取、打包和路由都是相關聯的。當 Create React App 釋出時,React 還很新,如何讓這些功能獨立工作都還有很多東西需要弄清楚,更不用說如何完美地將它們組合在一起了。

時代在發展,現在,越來越難以推薦無法獲得這些功能的解決方案。即使不立即使用它們,它們也應該在需要時可用,並且不必遷移到不同的模板並重新構建所有程式碼即可利用它們。 同樣,並非所有資料獲取或程式碼拆分都需要基於路由。但這是一個很好的預設設定,應該適用於大多數 React 應用。

雖然可以自己整合所有這些功能,但很難好。 就像 Create React App 本身集成了與編譯相關的幾個功能一樣,Next.js、Gatsby 和 Remix 等工具跟進一步——將編譯與渲染、路由和資料獲取整合在一起。這類集編譯、渲染、路由和資料獲取於一體的工具被稱為“框架”(或者,如果喜歡稱 React 為框架的話,可以稱它們為“元框架”)。這些框架提供了更好的使用者體驗。

React 作為一個架構

我們喜歡 React 的靈活性,可以使用 React 構建單個按鈕,也可以使用它構建整個應用。 可以使用它在已有 20 年曆史的 Perl 網站中構建儀表板,或者可以使用 React 製作混合 SSG/SSR 的電子商務網站。這種靈活性是必不可少的,使用者也很喜歡它。

React 團隊也希望為完全使用 React 構建的新應用提供更好的預設設定。如果預設建議的建立 React 應用的方法支援 SSG 和 SSR、自動程式碼拆分、路由預載入、保留客戶端 UI 狀態的導航以及其他可實現出色使用者體驗的功能,就太好了。至少,建立 React 應用的預設建議方式不應該完全被排除在這些功能之外,因為現有的僅客戶端架構沒有實現這些功能。

React 面臨著挑戰,幫助 React 框架提供出色使用者體驗的最佳方式就是專注於 React 的底層。 React 本身可以在渲染層做一些獨特的事情,這些事情大大提高了框架在其他層的能力。例如,與<Suspense>一樣,一個 React API 可以在幕後為框架解鎖一系列框架優化。

React 是一個庫,它提供了一些 API,可讓定義和組合元件。 React 也是一種架構,它提供了讓框架作者充分利用其渲染模型的構建塊。我們可以在沒有框架的情況下使用 React。 但需要確保,如果將它與框架一起使用,框架能夠充分利用 React。 在過去幾年中構建的許多功能(<Suspense>useTransition、流式 API(如 renderToPipeableStream 和實驗性的服務端元件)都是面向框架的。 它們讓框架通過整合打包、路由和資料獲取來充分利用 React。

可以看到,Next 13、Gatsby 5 和 Remix 1.11 中採用了其中一些功能。還有很多工作要做,其中一些工作正在從實驗階段結束。 儘管如此,React 團隊還是很高興看到多年的努力得到了回報,並使 React 框架(及其使用者)能夠釋出更快的應用。

一個庫,多個框架

React 生態系統更適合擁有眾多競爭,有多種競爭的資料獲取解決方案和路由解決方案。這些選擇每年都會變得更好。也有多種整合路由、資料獲取、渲染和編譯的解決方案——即多個 React 框架。

我們希望保持這種狀態,也希望在可能的情況下鼓勵融合並使 React 生態系統從中受益。例如,不同的框架可能使用不同的機制來載入資料。但是,如果它們都採用 <Suspense> 作為載入指示器,那麼在 <Suspense> 中的更高級別的功能將適用於所有框架。

如果大多數 React 應用的最佳方式是從一個框架開始,那我們應該建議使用哪個框架? 我們應該選擇一個嗎? 我們如何決定選擇哪一個? 如果它隨著時間的推移停滯不前怎麼辦? 這就引出了上面提到的問題。

我們應該用 Create React App 做什麼?

Create React App 最初的目標是:

  • 提供一種無需配置即可啟動新 React 專案的簡單方法;
  • 整合編譯相關依賴,方便升級;
  • 讓 React 團隊儘可能廣泛地部署工具更新(例如快速重新整理支援、Hooks lint 規則)。

然而,它不再滿足最初的目標,即成為建立 React 應用的最佳方式。通過提高標準並將編譯與渲染、路由和資料獲取相整合,框架可以讓使用者建立 React 應用時:

  • 充分利用 Web API 來預設提供快速的應用和網站,無論大小;
  • 充分利用 React 及其框架級功能;
  • 提供路由和資料獲取。

React 生態系統值得推薦一種預設的方法,它可以充分利用 Web 和 React 本身。這甚至並不意味著一定要依賴於 Node.js 伺服器。 許多流行的框架不需要伺服器並且可以在 SSG 模式下工作,因此它們也可以解決“完全靜態”的用例。 框架的好處就是,如果以後需要 SSR,不需要進行遷移,它和其他功能一樣開箱即用(例如,Remix 提供了開箱即用的 mutation API)。

那該如何實現這一願景?有以下選擇:

選項 1:從頭開始建立一個新框架

可以嘗試將 Create React App 重新設計架構成為一個整合資料獲取、路由、打包和 SSG/SSR 的框架。構建一個高質量的新框架是一項艱鉅的任務,需要大量的生態系統專業知識,即使停止其他專案來實現這一目標,也會存在著隨著時間的推移出現停滯不前的重大風險,就像 Create React App 一樣。它還會進一步分裂生態系統,儘管沒有真正的使用者。 所以認為這個選專案前不實用。

選項 2:棄用 Create React App,維護一個Vite模板

可以棄用 Create React App,維護自己的 Vite 模板。 為了實現這個目標,該模板必須非常複雜。 事實上,它必須像 React 框架一樣複雜——並且需要整合路由、資料獲取等功能。這導致了同樣的問題:實際上是在建立另一個框架。

選項 3:棄用 Create React App,建議使用 React 框架

可以不再強調或反對將 Create React App 作為工具,而是更積極地強調 React 框架。這並不意味著必須使用其中一個React框架,但建議在大多數應用中使用其中一個框架。不利的一面就是,這將影響 React 的品牌(“為什麼不推薦建立 React 應用?”)。

選項 4:讓 Create React App 使用單一框架

可以選擇一個指定框架,並更改 Create React App 以預設使用該框架建立應用。這種方法的主要問題是它使其他解決方案很難競爭——尤其是當它們的權衡略有不同,但在流行度、功能集和質量方面大致相同時。 這種行為上的改變也是具有破壞性的,因為所有的舊教程都會以一種不明顯的方式中斷。

選項 5:將 Create React App 變成啟動器

可以將 Create React App 保留為命令,但將其變成啟動器。它將建議一個推薦框架列表,然後是“經典”無框架方法。“經典”方法將產生一個像 CRA 現在這樣的客戶端專用應用(以避免破壞已有教程),但內部最終可能會使用 Vite。

要想進入精選框架列表,React 框架必須滿足特定條件。需要考慮社群的流行度和採用率(以保持列表簡短)、功能集、效能特徵、充分利用 Web 平臺和 React 本身的能力、它是否得到積極維護以及是否清楚如何在各種託管服務和環境中託管它。每個框架的入門模板將由 React 團隊維護,以確保它們具有一致的設計和品牌,不連結到商業服務,並且結構相似。需要向社群清楚地傳達是如何做出這些選擇的,並且會定期重新評估它們。

React 團隊的建議

React 團隊目前傾向於選項 5(“將 Create React App 變成啟動器”)。 Create React App 的最初目標是為大多數 React 使用者提供啟動新的 React web 應用的最佳方式。重新調整它的用途,啟動器明確傳達了我們認為最適合大多數新 Web 應用的轉變。與選項 3 不同,它避免了“建立一個 React 應用”在某種程度上被棄用的看法。

React 團隊將制定更詳細的 RFC 提案,以充實這些要點。同時,希望聽到對這些問題的更多反饋。

對於使用 Vite 替換 Create React App,你有什麼看法?歡迎在評論區分享~

參考:https://github.com/reactjs/reactjs.org/pull/5487#issuecomment-1409720741