Server vs. Client Code Execution
このコンテンツはまだ日本語訳がありません。
服务器与客户端代码执行
Remix 可以在服务器和浏览器上运行您的应用。但是,它不会在两个地方运行您的所有代码。
在构建步骤中,编译器会创建服务器构建和客户端构建。服务器构建将所有内容打包成一个模块(或使用 服务器捆绑包 时打包成多个模块),但客户端构建会将您的应用拆分成多个捆绑包,以优化浏览器中的加载。它还会从捆绑包中删除服务器代码。
以下路由导出及其中使用的依赖项已从客户端构建中删除:
考虑上一节中的这个路由模块:
import type { ActionFunctionArgs, HeadersFunction, LoaderFunctionArgs,} from "@remix-run/node"; // or cloudflare/denoimport { json } from "@remix-run/node"; // or cloudflare/denoimport { useLoaderData } from "@remix-run/react";
import { getUser, updateUser } from "../user";
export const headers: HeadersFunction = () => ({ "Cache-Control": "max-age=300, s-maxage=3600",});
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 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 });}
服务器构建将在最终包中包含整个模块。但是,客户端构建将删除 action
、headers
和 loader
以及依赖项,结果如下:
import { useLoaderData } from "@remix-run/react";
export default function Component() { const user = useLoaderData(); return ( <Form 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> );}
拆分客户端和服务器代码
开箱即用,Vite 不支持在同一模块中混合服务器专用代码和客户端安全代码。 Remix 能够为路由设置例外,因为我们知道哪些导出是服务器专用的,并且可以将它们从客户端中删除。
有几种方法可以隔离 Remix 中的服务器专用代码。
最简单的方法是使用 .server
和 .client
模块。
.server
模块
虽然不是绝对必要的,但 .server
模块 是将整个模块明确标记为仅限服务器的好方法。
如果 .server
文件或 .server
目录中的任何代码意外地出现在客户端模块图中,则构建将失败。
app├── .server 👈 marks all files in this directory as server-only│ ├── auth.ts│ └── db.ts├── cms.server.ts 👈 marks this file as server-only├── root.tsx└── routes └── _index.tsx
.server
模块必须位于你的 Remix 应用目录中。
.server
目录仅在使用 Remix Vite 时受支持。Classic Remix Compiler 仅支持.server
文件。
.client
模块
您可能依赖于不安全的客户端库,甚至无法在服务器上捆绑 - 也许它试图通过简单的导入来访问 window
。
您可以通过在文件名后附加 *.client.ts
或将其嵌套在.client
目录中来从服务器构建中删除这些模块的内容。
.client
目录仅在使用 Remix Vite 时受支持。Classic Remix Compiler 仅支持.client
文件。
vite-env-only
如果您想在同一个模块中混合服务器专用代码和客户端安全代码,您可以使用undefined
。
例如,一旦将插件添加到 Vite 配置,您就可以使用 serverOnly$
包装任何仅服务器的导出:
import { serverOnly$ } from "vite-env-only";
import { db } from "~/.server/db";
export const getPosts = serverOnly$(async () => { return db.posts.findMany();});
export const PostPreview = ({ title, description }) => { return ( <article> <h2>{title}</h2> <p>{description}</p> </article> );};
此示例将被编译为客户端的以下代码:
export const getPosts = undefined;
export const PostPreview = ({ title, description }) => { return ( <article> <h2>{title}</h2> <p>{description}</p> </article> );};