コンテンツにスキップ

Fullstack Data Flow

このコンテンツはまだ日本語訳がありません。

全栈数据流

Remix 的主要功能之一是它能够自动保持 UI 与持久服务器状态同步。它分为三个步骤:

  1. 路由加载器向 UI 提供数据
  2. 表单发布数据以路由更新持久状态的操作
  3. 页面上的加载器数据会自动重新验证

路由模块导出

让我们考虑一个用户帐户编辑路由。路由模块有三个导出,我们将填写并讨论它们:

routes/account.tsx
export async function loader() {
// provides data to the component
}
export default function Component() {
// renders the UI
}
export async function action() {
// updates persistent data
}

路由 Loader

路由文件可以导出一个 loader 函数,为路由组件提供数据。当用户导航到匹配的路由时,会先加载数据,然后渲染页面。

routes/account.tsx
import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
export async function loader({
request,
}: LoaderFunctionArgs) {
const user = await getUser(request);
return json({
displayName: user.displayName,
email: user.email,
});
}
export default function Component() {
// ...
}
export async function action() {
// ...
}

路由组件

路由文件默认导出的是渲染的组件,通过 useLoaderData 读取 loader 数据:

import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { useLoaderData, Form } from "@remix-run/react";
export async function loader({
request,
}: LoaderFunctionArgs) {
const user = await getUser(request);
return json({
displayName: user.displayName,
email: user.email,
});
}
export default function Component() {
const user = useLoaderData<typeof loader>();
return (
<Form method="post" action="/account">
<h1>Settings for {user.displayName}</h1>
<input
name="displayName"
defaultValue={user.displayName}
/>
<input name="email" defaultValue={user.email} />
<button type="submit">Save</button>
</Form>
);
}
export async function action() {
// ...
}

路由行动

最后,在提交表单时,将调用与表单的 action 属性匹配的路由上的操作。在此示例中,它是相同的路由。表单字段中的值将在标准 request.formData() API 上可用。请注意,输入上的 name 属性与 formData.get(fieldName) getter 耦合。

import type {
ActionFunctionArgs,
LoaderFunctionArgs,
} from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { useLoaderData, Form } from "@remix-run/react";
export async function loader({
request,
}: LoaderFunctionArgs) {
const user = await getUser(request);
return json({
displayName: user.displayName,
email: user.email,
});
}
export default function Component() {
const user = useLoaderData<typeof loader>();
return (
<Form method="post" action="/account">
<h1>Settings for {user.displayName}</h1>
<input
name="displayName"
defaultValue={user.displayName}
/>
<input name="email" defaultValue={user.email} />
<button type="submit">Save</button>
</Form>
);
}
export async function action({
request,
}: ActionFunctionArgs) {
const formData = await request.formData();
const user = await getUser(request);
await updateUser(user.id, {
email: formData.get("email"),
displayName: formData.get("displayName"),
});
return json({ ok: true });
}

提交和重新验证

当用户提交表单时:

  1. Remix 通过 fetch 将表单数据发送到路由操作,待处理状态通过 useNavigationuseFetcher 等钩子变为可用。
  2. 操作完成后,重新验证加载器以获取新的服务器状态。
  3. useLoaderData 从服务器返回更新的值,待处理状态返回空闲状态。

通过这种方式,UI 就与服务器状态保持同步,而无需为该同步编写任何代码。

除了 HTML 表单元素之外,还有多种方式提交表单(例如响应拖放或 onChange 事件)。还有更多关于表单验证、错误处理、待处理状态等的内容需要讨论。我们稍后会讲到所有这些内容,但这是 Remix 中数据流的要点。

JavaScript 加载之前

当您从服务器发送 HTML 时,最好在 JavaScript 加载之前就让它工作。Remix 中的典型数据流会自动执行此操作。流程相同,但浏览器会执行部分工作。

当用户在 JavaScript 加载之前提交表单时:

  1. 浏览器将表单提交给操作(而不是fetch),并且浏览器的待处理状态激活(旋转图标)
  2. 操作完成后,加载器被调用
  3. Remix 呈现页面并将 HTML 发送到浏览器