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
相关产品推荐
相关产品推荐

