React Router Logo
2022 年 11 月 2 日

React Router 與 Remix 的整合

Matt Brophy
資深開發人員

今年稍早,我們開始著手將 React Router 與 Remix 整合,目標是將所有 Remix Data API(loadersactionsfetchers 等)移至 React Router。隨著最近 React Router v6.4.0 的發布,我們很自豪地宣布,我們已完成這項工作...而且我們認為我們讓它們變得更好了😃。我們不僅修復了一些邊緣案例的錯誤,還穩定了一些 API 並引入了一些非常棒的新 API。以下是變更的快速概述,但我們鼓勵您查看部落格文章以獲取更多資訊。

  • 🆕 使用 useRevalidator 程式化地重新驗證
  • 🆕 使用 defer/Await 分隔您的重要/非重要資料
  • 🆕 使用 useRouteLoaderData 取得特定路由的 loader 資料
  • unstable_shouldReload 已穩定為 shouldRevalidate
  • 🔥 全新且改良的 <ScrollRestoration getKey> 方法可對捲動還原進行更精細的控制
  • 🔥 Catch 和 Error 邊界已合併為單一的 errorElement
  • 🐞 fetcher.load 呼叫現在參與重新驗證 - 就像它們一直應該做的那樣!

React Router 與 Remix 的整合

現在,我們要反過來,開始 **React Router 與 Remix 的整合**,以便我們可以將這些變更帶回給 Remix 使用者(並在這個過程中刪除*一大堆* Remix 程式碼)。對你們所有人來說好消息是,我們計劃以迭代的方式進行,而不會進行重大版本發布🤯。我們認為我們使用的方法非常酷,因此我們想與大家分享。

您可以將 Remix 的架構視為具有 4 個主要方面

  1. 伺服器導航 + 資料擷取
  2. 伺服器 HTML 渲染
  3. 用戶端 hydration
  4. 用戶端導航 + 資料擷取

這 4 個部分也恰好被很好地解耦,因此它們為我們提供了清晰的邊界,可以以迭代的方式處理這個問題,以避免單一的大爆炸式發布。這應該意味著 Remix 使用者的整合路徑更加順暢!

步驟 1 - 伺服器導航和資料擷取

我們首先會更新 Remix 的伺服器執行階段,以使用 React Router 的新 unstable_createStaticHandler 來進行伺服器端資料擷取,一旦我們感到放心,我們就可以發布它,而無需觸及步驟 2 到 4。更好的是,我們可以將其分解為資源路由請求、用戶端導航資料擷取請求和文件請求的個別工作。

注意:createStaticHandler6.4.0 中發布為不穩定版本,以防我們在此過程中遇到所需的變更。我們會在完成 Remix 整合後將其穩定化。

我們計劃使用 Martin Fowler 的 絞殺榕模式逐一執行這些操作,以便我們可以高度確信我們沒有引入任何回歸(感謝 @DavidKPiano 在我腦海中重新浮現這個模式 幾個月前!)。如果您不熟悉這種模式,它的總體要點是您將新程式碼與舊程式碼一起編寫,然後逐步切換部分。我們可以使用功能標誌方法進一步推進,該方法保持兩個路徑都處於活動狀態,並允許在測試期間和執行階段進行驗證。

以下是一個簡化的範例,說明在 Remix 中資源路由請求的情況可能如下所示

function handleResourceRouteRequest({ request }) {
  // If the flag is enabled, clone the request so we can use it twice
  let response = processResourceRouteRequest(
    ENABLE_NEW_STUFF ? request.clone() : request,
  );

  // When our flag is enabled, send this request through the new
  // code path, while also asserting that we get back an identical
  // response
  if (ENABLE_NEW_STUFF) {
    let newResponse = processResourceRouteRequestNew(request);
    assertResponses(response, newResponse);
    return newResponse;
  }

  return response;
}

這種方法為我們帶來了許多好處

  • 功能標誌允許我們在建置時剝離新程式碼路徑,因此我們不會在發布的 remix 版本中包含任何新的程式碼(直到我們準備好)
  • 因此,我們可以將此程式碼直接合併到 dev 分支中,而不是維護長期存在的功能分支
  • 斷言可用於我們所有的單元和整合測試,以確保沒有任何問題,也可以在執行階段為本地應用程式開發啟用,這使我們能夠更好地在實際的 Remix 應用程式上測試
  • 一旦我們確信新程式碼路徑已準備就緒,我們可以將功能標誌從「執行兩者」的方法更改為「執行一個或另一個」的方法,這為我們提供了一個非常快速的回滾策略,以防我們在發布新程式碼路徑時出現任何問題
  • 最後,功能標誌為我們提供了清晰且易於參考的內容,以便在我們完成所有操作後刪除 🪓

步驟 2 - 伺服器 HTML 渲染

一旦我們完成伺服器端資料擷取,我們可以轉到伺服器端 HTML 渲染(使用 React Router 的 unstable_StaticRouterProvider)。這是我們可以(某種程度上)獨立執行的另一個方面。我們可以使用新的 API 在伺服器上渲染 HTML,但是我們將無法在用戶端(使用舊的 API)上對其進行 hydration,而無需一些糟糕的程式碼分支來確定是從舊的還是新的上下文讀取。值得慶幸的是,慣用的 Remix 應用程式可以在沒有 JavaScript 的情況下運作,因此我們可以在此步驟中驗證我們的測試和應用程式,而無需 JS。顯然我們不會在步驟 2 之後發布,但是我們將能夠在進入步驟 3 之前對我們的 SSR 獲得相當高的信心。我們將再次使用標誌來啟用這兩個路徑,並在舊的和新的之間執行某種程度的 HTML 斷言。

這裡有趣的部分是,此步驟是我們開始充分認識到 Michael 的願景,即 Remix 是一個 「React Router 的編譯器」。現在,react-router 知道如何執行所有酷炫的資料擷取工作,Remix 只會將磁碟上的一組慣例路由檔案編譯為 React Router 期望的適當路由。一旦建立了這些路由,它就會將它們交給 React Router 進行繁重的工作💪!

步驟 3 - 用戶端 hydration

進入用戶端 hydration!這就是我們將刪除絕大部分 Remix 程式碼的地方(再見 Transition Manager - 我們會永遠愛你 🙃)。與上述類似,Remix 只需要利用伺服器提供的路由資訊清單來生成一個路由樹,以交給 createBrowserRouter,然後 RouterProvider 完成其餘的工作。*超級*酷的部分是,Remix 可以為其所有路由使用*完全相同的 loader 和 action*,因為它們所做的只是使用 _data 參數向 Remix 伺服器發出 fetch 請求!這可能不會通過功能標誌,因為我們不能完全對文件進行兩次 hydration 🤷‍♂️。

步驟 4 - 用戶端導航和資料擷取

這可能是軟體開發中最不正確的主張,但我們將在此處固執地再次使用它 - 一旦我們完成步驟 3,用戶端路由和資料擷取應該**Just Work™️**,因為現在這完全由 React Router 6.4 處理!

回溯相容性

我們應該再次注意,我們計劃將所有這些作為次要的 Remix 1.x 版本發布(步驟 1 可能一個版本,步驟 2-4 另一個版本)。為了保持回溯相容性,在步驟 2 和 3 中需要完成一些工作。以下是一些範例

  • useTransition 在 React Router 6.4 中已重新命名為 useNavigation,因此我們會將 useTransition 標記為已棄用,但會保留它
  • React Router 將 submission 欄位扁平化,並從導航(以前的轉換)和擷取器中刪除了 type 欄位,因此我們將使用包裝器 hook 將這些欄位加回 useTransitionuseFetcher
  • React Router 沒有單獨的 Error 和 Catch 邊界的概念,因此我們將確保它們保持功能正常

在可能的情況下(我們希望所有情況都是如此),我們將在 remix.config.js 中新增功能標誌,讓您可以根據自己的方便選擇加入新的 Remix v2 行為,同時在您不選擇加入的情況下提供回溯相容的行為。

結語

我們對下一步感到非常興奮,並且對它為 React Router 和 Remix 的未來打開的一些可能性(有人說 Preact 嗎?)感到更加興奮。請密切關注 repo 以獲取更新,並且一如既往,如果您對任何或所有這些內容有任何疑問或興奮,請在 DiscordTwitter 上與我們聯繫:)


獲取有關最新 Remix 新聞的更新

成為第一個了解新 Remix 功能、社群活動和教學課程的人。