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

Next.js应用部署Vercel后删除操作页面渲染异常求助

问题描述

本地测试时Next.js应用功能正常,但部署到Vercel(搭配Postgres数据库)后,创建项目/技能功能正常,删除操作异常:数据已从数据库删除,但页面未正确更新,还会出现其他项目凭空消失(实际仍在数据库)的情况。


相关代码

skill-view-card.tsx

'use client'

import { deleteSkill, createProject, deleteProject } from '@/app/lib/actions';
import { Button } from '@nextui-org/react';
import { TrashIcon } from '@heroicons/react/20/solid';


export default function SkillViewCard({ skills, projects }: { skills: string; projects: string }) { // 操作后这里获取的是旧数据
  const skillsParsed = JSON.parse(skills); // 需要重新获取数据
  const projectsParsed = JSON.parse(projects);

  function toTitleCase(str: string) {
    return str.replace(/\w\S*/g, (text: string) => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase());
  }

  return (
    <div className="p-8 border w-full h-full shadow-lg rounded-md bg-white">
      <div className="columns-2">
        <div className="text-left text-xl font-bold">
          {toTitleCase(skillsParsed?.description) || '暂无描述'}
        </div>
        <Button onClick={() => { deleteSkill(skillsParsed.id);}}>
          <TrashIcon className="h-4 w-4 content-right" />
        </Button>
      </div>
      <div className="columns-1">
        <fieldset>
          {projectsParsed && projectsParsed.length > 0 ? (
            projectsParsed
              .filter((project: { skill_id: string }) => project.skill_id === skillsParsed?.id)
              .map((project: { title: string, id: number }) => (
                <div key={project.id}>
                  <div>{project.title}</div>
                  <Button onClick={() => { deleteProject(project.id);}}>
                    <p>删除</p>
                  </Button>
                </div>
              ))
          ) : (
            <p className="text-gray-500">暂无项目</p>
          )}
        </fieldset>
      </div>
      <div className="columns-2"></div>
      <input
                type="text"
                placeholder="输入项目标题"
                className="border rounded-md p-2 w-half"
                onKeyDown={(e) => {
                    if (e.key === 'Enter') {
                        e.preventDefault();
                        const formData = new FormData();
                        formData.append('title', e.currentTarget.value);
                        formData.append('skill_id', skillsParsed.id);
                        createProject(formData);
                        e.currentTarget.value = '';
                    }
                }}
            />
        </div>
  );
}

@/app/lib/actions.ts

'use server';
import { sql } from '@vercel/postgres';
import { redirect } from 'next/navigation';

export async function createSkill(formData: FormData) {
    const skillDescription = formData.get('skill') as string | null;

    await sql`
    INSERT INTO skills (description)
    VALUES (${skillDescription || ''})
  `;

  redirect('/test-page?refetch=true'); // 这里需要重新获取技能数据
}

export async function deleteSkill(id: number) {
    await sql`
    DELETE FROM skills WHERE id = ${id}
    `;

    redirect('/test-page?refetch=true');
}

export async function createProject(formData: FormData) {
    const projectTitle = formData.get('title') as string | null;
    const skillId = formData.get('skill_id') ? parseInt(formData.get('skill_id') as string, 10) : null;

    await sql`
    INSERT INTO projects (title, skill_id)
    VALUES (${projectTitle || ''}, ${skillId || ''})
  `;

  redirect('/test-page?refetch=true');
}

export async function deleteProject(id: number) {
    await sql`
    DELETE FROM projects WHERE id = ${id}
  `;
  redirect('/test-page?refetch=true');
}

问题根源与解决方案

核心问题:Vercel生产环境的静态缓存

本地开发默认禁用缓存,而Vercel为了性能会对Next.js服务器组件做静态优化缓存。即便删除后重定向带refetch=true参数,服务器仍可能返回缓存的旧数据,导致页面显示异常。

解决步骤

  1. 强制页面动态渲染
    在test-page页面组件顶部添加配置,禁用静态缓存,确保每次请求都从数据库拉取最新数据:
export const dynamic = 'force-dynamic'; // 强制页面每次请求都动态执行数据查询

或者设置无缓存有效期:

export const revalidate = 0; // 效果等同于force-dynamic
  1. 确保父组件数据是最新的
    SkillViewCard作为客户端组件,接收的skills和projects由父服务器组件传递。确保父组件的查询逻辑是动态执行的,比如:
// test-page.tsx 示例
import { sql } from '@vercel/postgres';
import SkillViewCard from './skill-view-card';

export default async function TestPage() {
  // 每次请求都查询最新数据
  const { rows: skills } = await sql`SELECT * FROM skills`;
  const { rows: projects } = await sql`SELECT * FROM projects`;

  return (
    <div className="grid grid-cols-3 gap-4">
      {skills.map(skill => (
        <SkillViewCard 
          key={skill.id} 
          skills={JSON.stringify(skill)} 
          projects={JSON.stringify(projects)} 
        />
      ))}
    </div>
  );
}

export const dynamic = 'force-dynamic';
  1. 验证重定向逻辑
    删除操作后的重定向本身没问题,但必须配合页面动态渲染配置才能生效,否则缓存会覆盖新数据的查询结果。

额外说明

本地和生产环境的缓存差异是问题的核心,通过强制动态渲染,就能让页面始终展示数据库中的最新状态,解决删除后页面更新异常的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 13:54:54