Remix 在 v2.4.0
中引入了對「客戶端資料」的支援(RFC),這讓您可以選擇透過路由中的 clientLoader
/clientAction
匯出,在瀏覽器中執行路由載入器/動作。
這些新的匯出功能是一把鋒利的刀,不建議作為您的主要資料載入/提交機制,而是讓您可以在以下某些進階用例中加以運用
請謹慎使用這些新的匯出功能!如果您不小心,很容易讓您的 UI 失去同步。Remix 開箱即用,會非常努力確保這種情況不會發生,但是一旦您控制了自己的客戶端快取,並可能阻止 Remix 執行其正常的伺服器 fetch
呼叫,那麼 Remix 將無法再保證您的 UI 保持同步。
當在 BFF 架構中使用 Remix 時,跳過 Remix 伺服器中繼點並直接存取您的後端 API 可能會很有利。這假設您可以適當地處理身份驗證,並且不受 CORS 問題的影響。您可以如下跳過 Remix BFF 中繼點
loader
載入資料clientLoader
載入資料在這種情況下,Remix 不會在水合 (hydration) 時呼叫 clientLoader
,而只會在後續導覽時呼叫它。
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import type { ClientLoaderFunctionArgs } from "@remix-run/react";
export async function loader({
request,
}: LoaderFunctionArgs) {
const data = await fetchApiFromServer({ request }); // (1)
return json(data);
}
export async function clientLoader({
request,
}: ClientLoaderFunctionArgs) {
const data = await fetchApiFromClient({ request }); // (2)
return data;
}
有時,您可能想要利用「全堆疊狀態」,其中某些資料來自伺服器,而某些資料來自瀏覽器(即 IndexedDB
或其他瀏覽器 SDK),但在您擁有完整的資料集之前,無法呈現您的元件。您可以如下組合這兩個資料來源
loader
載入部分資料HydrateFallback
元件,以便在 SSR 期間呈現,因為我們尚未擁有完整的資料集clientLoader.hydrate = true
,這會指示 Remix 在初始文件水合期間呼叫 clientLoaderclientLoader
中組合伺服器資料和客戶端資料import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import type { ClientLoaderFunctionArgs } from "@remix-run/react";
export async function loader({
request,
}: LoaderFunctionArgs) {
const partialData = await getPartialDataFromDb({
request,
}); // (1)
return json(partialData);
}
export async function clientLoader({
request,
serverLoader,
}: ClientLoaderFunctionArgs) {
const [serverData, clientData] = await Promise.all([
serverLoader(),
getClientData(request),
]);
return {
...serverData, // (4)
...clientData, // (4)
};
}
clientLoader.hydrate = true; // (3)
export function HydrateFallback() {
return <p>Skeleton rendered during SSR</p>; // (2)
}
export default function Component() {
// This will always be the combined set of server + client data
const data = useLoaderData();
return <>...</>;
}
您可能想要在您的應用程式中混合搭配資料載入策略,以便某些路由僅在伺服器上載入資料,而某些路由僅在客戶端上載入資料。您可以如下針對每個路由進行選擇
loader
clientLoader
和 HydrateFallback
僅依賴伺服器載入器的路由如下所示
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
export async function loader({
request,
}: LoaderFunctionArgs) {
const data = await getServerData(request);
return json(data);
}
export default function Component() {
const data = useLoaderData(); // (1) - server data
return <>...</>;
}
僅依賴客戶端載入器的路由如下所示。
import type { ClientLoaderFunctionArgs } from "@remix-run/react";
export async function clientLoader({
request,
}: ClientLoaderFunctionArgs) {
const clientData = await getClientData(request);
return clientData;
}
// Note: you do not have to set this explicitly - it is implied if there is no `loader`
clientLoader.hydrate = true;
// (2)
export function HydrateFallback() {
return <p>Skeleton rendered during SSR</p>;
}
export default function Component() {
const data = useLoaderData(); // (2) - client data
return <>...</>;
}
您可以利用客戶端快取(記憶體、本機儲存空間等)來繞過某些伺服器呼叫,如下所示
loader
載入資料clientLoader.hydrate = true
以初始化快取clientLoader
從快取載入後續導覽clientAction
中使快取失效請注意,由於我們沒有匯出 HydrateFallback
元件,我們將對路由元件進行 SSR,然後在水合時執行 clientLoader
,因此您的 loader
和 clientLoader
在初始載入時傳回相同的資料,以避免水合錯誤,這點非常重要。
import type {
ActionFunctionArgs,
LoaderFunctionArgs,
} from "@remix-run/node";
import { json } from "@remix-run/node";
import type {
ClientActionFunctionArgs,
ClientLoaderFunctionArgs,
} from "@remix-run/react";
export async function loader({
request,
}: LoaderFunctionArgs) {
const data = await getDataFromDb({ request }); // (1)
return json(data);
}
export async function action({
request,
}: ActionFunctionArgs) {
await saveDataToDb({ request });
return json({ ok: true });
}
let isInitialRequest = true;
export async function clientLoader({
request,
serverLoader,
}: ClientLoaderFunctionArgs) {
const cacheKey = generateKey(request);
if (isInitialRequest) {
isInitialRequest = false;
const serverData = await serverLoader();
cache.set(cacheKey, serverData); // (2)
return serverData;
}
const cachedData = await cache.get(cacheKey);
if (cachedData) {
return cachedData; // (3)
}
const serverData = await serverLoader();
cache.set(cacheKey, serverData);
return serverData;
}
clientLoader.hydrate = true; // (2)
export async function clientAction({
request,
serverAction,
}: ClientActionFunctionArgs) {
const cacheKey = generateKey(request);
cache.delete(cacheKey); // (4)
const serverData = await serverAction();
return serverData;
}
我們預計在 SPA 模式推出後,撰寫一份單獨的遷移指南,但目前我們預計此過程會像這樣
createBrowserRouter
/RouterProvider
,在您的 React Router SPA 中引入資料模式loader
函數都充當 clientLoader
loader
函數為 clientLoader
clientLoader
在客戶端中進行clientLoader -> loader
,以開始將資料載入移至伺服器