Remix 在服务器和浏览器中都运行您的应用程序。但是,它不会在两个地方都运行所有代码。
在构建步骤期间,编译器会创建服务器构建和客户端构建。服务器构建将所有内容捆绑到单个模块(或在使用服务器捆绑时捆绑到多个模块),但客户端构建会将您的应用程序拆分为多个捆绑以优化浏览器中的加载。它还会从捆绑中删除服务器代码。
以下路由导出及其在其中使用的依赖项将从客户端构建中删除
考虑上一节中的此路由模块
import type {
ActionFunctionArgs,
HeadersFunction,
LoaderFunctionArgs,
} from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno
import { 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 编译器仅支持.server
文件。
.client
模块您可能依赖于即使在服务器上捆绑也不安全的客户端库——也许它只是通过导入就尝试访问window
。
您可以通过在文件名后附加*.client.ts
或将它们嵌套在.client
目录中,从服务器构建中删除这些模块的内容。
.client
目录。经典 Remix 编译器仅支持.client
文件。
如果您想在同一个模块中混合服务器端代码和客户端安全代码,可以使用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>
);
};