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
目錄。Classic Remix Compiler 僅支援 .server
檔案。
.client
模組您可能會依賴即使在伺服器上綁定也不安全的客戶端程式庫 — 也許它只是因為被引入而嘗試存取 window
。
您可以透過在檔案名稱後附加 *.client.ts
或將它們巢狀在 .client
目錄中,從伺服器建置中移除這些模組的內容。
.client
目錄。Classic Remix Compiler 僅支援 .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>
);
};