You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中actions.ts无法同时使用'use server'与导出revalidate常量怎么办?

问题根源

带"use server"指令的文件属于Server Actions,这类文件仅允许导出异步函数(即Server Action),无法导出revalidate这类静态缓存配置常量——两者的作用场景完全冲突:revalidate是用于数据查询类函数的缓存刷新规则,而Server Actions是用于数据增删改的服务器端操作函数。

解决方案

方案1:拆分文件(推荐)

将数据查询函数与数据操作函数拆分到不同文件,各司其职:

1. 创建数据查询文件(如data/queries.ts)

存放所有查询类函数,不添加"use server",在这里设置文件级缓存重验证:

import { sql } from "@vercel/postgres";

// 文件级缓存规则:所有查询结果每1秒自动刷新
export const revalidate = 1;

export async function fetchProjects() {
  const { rows } = await sql`SELECT * FROM projects`;
  return rows;
}

export async function fetchProjectById(id: string) {
  const { rows } = await sql`SELECT * FROM projects WHERE id = ${id}`;
  return rows[0];
}

export async function fetchProjectMilestones(id: string) {
  const { rows } = await sql`SELECT * FROM milestones WHERE project_id = ${id}`;
  return rows;
}

2. 保留Server Actions文件(actions.ts)

仅存放数据增删改函数,添加"use server",通过revalidatePath手动刷新对应路由的缓存:

"use server";

import { sql } from "@vercel/postgres";
import { revalidatePath } from "next/cache";

export async function createProject(title: string, description: string ) {
  try {
    await sql`INSERT INTO projects (title, description) VALUES (${title}, ${description})`;
    // 刷新项目列表页缓存
    revalidatePath("/projects");
  } catch(error) {
    throw new Error("Failed to create new project.");
  }
}

export async function createMilestone(title: string, description: string, due_date: Date, project_id: string) {
  await sql`INSERT INTO milestones (title, description, due_date, project_id) VALUES (${title}, ${description}, ${due_date.toDateString()}, ${project_id})`;
  // 刷新对应项目详情页缓存(用实际ID替换动态路由参数)
  revalidatePath(`/projects/${project_id}`);
}

export async function deleteMilestone(id: string, project_id: string) {
  await sql`DELETE FROM milestones WHERE id = ${id};`;
  revalidatePath(`/projects/${project_id}`);
}

方案2:不拆分文件,用函数级缓存策略

如果不想拆分文件,用unstable_cache给每个查询函数单独设置缓存重验证规则,替代文件级的revalidate常量:

"use server";

import { sql } from "@vercel/postgres";
import { revalidatePath, unstable_cache } from "next/cache";

// 用unstable_cache包裹查询函数,设置1秒重验证
export const fetchProjects = unstable_cache(
  async () => {
    const { rows } = await sql`SELECT * FROM projects`;
    return rows;
  },
  ["projects"], // 缓存唯一标识,用于区分不同缓存条目
  { revalidate: 1 }
);

export const fetchProjectById = unstable_cache(
  async (id: string) => {
    const { rows } = await sql`SELECT * FROM projects WHERE id = ${id}`;
    return rows[0];
  },
  ["project", id], // 加入ID作为缓存标识的一部分,确保不同项目缓存独立
  { revalidate: 1 }
);

export const fetchProjectMilestones = unstable_cache(
  async (id: string) => {
    const { rows } = await sql`SELECT * FROM milestones WHERE project_id = ${id}`;
    return rows;
  },
  ["milestones", id],
  { revalidate: 1 }
);

// 数据操作函数保持不变
export async function createProject(title: string, description: string ) {
  try {
    await sql`INSERT INTO projects (title, description) VALUES (${title}, ${description})`;
    revalidatePath("/projects");
  } catch(error) {
    throw new Error("Failed to create new project.");
  }
}

export async function createMilestone(title: string, description: string, due_date: Date, project_id: string) {
  await sql`INSERT INTO milestones (title, description, due_date, project_id) VALUES (${title}, ${description}, ${due_date.toDateString()}, ${project_id})`;
  revalidatePath(`/projects/${project_id}`);
}

export async function deleteMilestone(id: string, project_id: string) {
  await sql`DELETE FROM milestones WHERE id = ${id};`;
  revalidatePath(`/projects/${project_id}`);
}

额外注意

原代码中revalidatePath("/projects/[slug]")是无效的,动态路由参数需要替换为实际的project_id,或用/projects/(.*)匹配所有项目详情页路由。

内容的提问来源于stack exchange,提问作者kool

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 13:07:33