框架教程
本页内容

.server 模块

摘要

仅在服务器上运行,并从客户端包中排除的仅服务器模块。

// This would expose secrets on the client if not exported from a server-only module
export const JWT_SECRET = process.env.JWT_SECRET;

export function validateToken(token: string) {
  // Server-only authentication logic
}

.server 模块是明确将整个模块标记为仅限服务器使用的一种好方法。如果 .server 文件或 .server 目录中的任何代码意外地出现在客户端模块图中,构建将会失败。

路由模块不应标记为 .server.client,因为它们有特殊处理,需要在服务器和客户端模块图中引用。尝试这样做会导致构建错误。

如果您需要对客户端/服务器包中包含的内容进行更精细的控制,请查看 vite-env-only 插件

用法模式

单个文件

通过在文件名中添加 .server 将单个文件标记为仅服务器使用

app/
├── auth.server.ts         👈 server-only file
├── database.server.ts
├── email.server.ts
└── root.tsx

服务器目录

通过在目录名称中使用 .server 将整个目录标记为仅服务器使用

app/
├── .server/               👈 entire directory is server-only
│   ├── auth.ts
│   ├── database.ts
│   └── email.ts
├── components/
└── root.tsx

示例

数据库连接

import { PrismaClient } from "@prisma/client";

// This would expose database credentials on the client
const db = new PrismaClient({
  datasources: {
    db: {
      url: process.env.DATABASE_URL,
    },
  },
});

export { db };

身份验证工具

import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";

const JWT_SECRET = process.env.JWT_SECRET!;

export function hashPassword(password: string) {
  return bcrypt.hash(password, 10);
}

export function verifyPassword(
  password: string,
  hash: string
) {
  return bcrypt.compare(password, hash);
}

export function createToken(userId: string) {
  return jwt.sign({ userId }, JWT_SECRET, {
    expiresIn: "7d",
  });
}

export function verifyToken(token: string) {
  return jwt.verify(token, JWT_SECRET) as {
    userId: string;
  };
}

使用服务器模块

import type { ActionFunctionArgs } from "react-router";
import { redirect } from "react-router";
import {
  hashPassword,
  createToken,
} from "../utils/auth.server";
import { db } from "../utils/db.server";

export async function action({
  request,
}: ActionFunctionArgs) {
  const formData = await request.formData();
  const email = formData.get("email") as string;
  const password = formData.get("password") as string;

  // Server-only operations
  const hashedPassword = await hashPassword(password);
  const user = await db.user.create({
    data: { email, password: hashedPassword },
  });

  const token = createToken(user.id);

  return redirect("/dashboard", {
    headers: {
      "Set-Cookie": `token=${token}; HttpOnly; Secure; SameSite=Strict`,
    },
  });
}

export default function Login() {
  return (
    <form method="post">
      <input name="email" type="email" required />
      <input name="password" type="password" required />
      <button type="submit">Login</button>
    </form>
  );
}
文档和示例 CC 4.0
编辑