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

Next.js 14中基于谷歌表格实现ISR(无fetch)的问题求助

问题:Next.js 14 实现基于Google Sheets的增量静态再生(ISR)

我需要给Web应用的schedule页面实现ISR,要求每3000秒重新从Google Spreadsheets拉取日程数据(用户填表后数据会同步到表格)。当前用Next.js 14(不支持getServerSideProps),之前在单独文件app/agendaData.js里获取数据再在page.tsx调用,试过export const revalidate = 3000和force_cache,但页面始终是静态构建的(因为Google API没用到fetch)。按照文档要求把数据逻辑移到了app/schedule/page.js里,当前代码如下,求帮忙搞定ISR:

const serviceAccountAuth = new JWT({
   //google api credentials
  scopes: [
    'https://www.googleapis.com/auth/spreadsheets',
  ],
});


const theData= unstable_cache(async () => {
  try {

    const doc = new GoogleSpreadsheet(process.env.GOOGLE_SHEET_ID, serviceAccountAuth);

    await doc.loadInfo()


    const sheet = doc.sheetsByIndex[0];
    const rows = await sheet.getRows();


    let rowData: any[] | PromiseLike<any[] | undefined> | undefined = [];

    rows.map((row, index = 0) => {
      let rowNum = row['_rowNumber'];
      let day = row['_rawData'].shift()


      let newObj = {
        'id': rowNum,
        'day': day,
        'data': row['_rawData']

      }
 
      rowData.push(newObj);
    })

    return rowData;
  }

  catch (error) {
    console.log(error)
    await theData()
  }

}, { 
  revalidate: 60})




// @ts-ignore
// @ts-ignore

export default async function Schedule(){
  let data;
  do {
    try {
      data = await theData()
    } catch (error){
      console.log(error)
    }} while (data === undefined)
  let day1 = data.filter(item => item['day'] === '1');
  // @ts-ignore
  let day2 = data.filter(item => item['day'] === '2');
  // @ts-ignore
  let day3 = data.filter(item => item['day'] === '3')
 
  return HTML

}

解决方案

核心问题分析

你的代码没触发ISR的原因有三个:一是unstable_cache缺少缓存键(Next.js需要它来管理缓存生命周期);二是错误处理里的无限递归可能导致缓存逻辑异常;三是页面级的revalidate配置未添加。

修改后的完整代码

import { JWT } from 'google-auth-library';
import { GoogleSpreadsheet } from 'google-spreadsheet';

const serviceAccountAuth = new JWT({
  // 替换为你的Google服务账号凭证
  email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,
  key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'),
  scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});

// 配置带唯一缓存键的unstable_cache
const fetchAgendaData = unstable_cache(async () => {
  try {
    const doc = new GoogleSpreadsheet(process.env.GOOGLE_SHEET_ID, serviceAccountAuth);
    await doc.loadInfo();
    const sheet = doc.sheetsByIndex[0];
    const rows = await sheet.getRows();

    const rowData = [];
    // 用forEach替代map,避免不必要的数组生成
    rows.forEach((row) => {
      const rowNum = row._rowNumber;
      const day = row._rawData.shift();
      rowData.push({
        id: rowNum,
        day,
        data: row._rawData,
      });
    });

    return rowData;
  } catch (error) {
    console.error('拉取Google Sheets数据失败:', error);
    // 返回空数组避免页面崩溃,不要递归调用自身
    return [];
  }
}, ['agenda-schedule-data'], { // 唯一缓存键,必须设置
  revalidate: 3000, // 3000秒后缓存失效,和页面ISR时间保持一致
});

// 页面级ISR配置,告诉Next.js该页面需要定时重新生成
export const revalidate = 3000;

export default async function Schedule() {
  const data = await fetchAgendaData();
  
  const day1 = data.filter(item => item.day === '1');
  const day2 = data.filter(item => item.day === '2');
  const day3 = data.filter(item => item.day === '3');

  // 替换成你的实际JSX渲染逻辑
  return (
    <div className="schedule-container">
      <h1>日程表</h1>
      <section>
        <h2>第一天</h2>
        {day1.map(item => (
          <div key={item.id} className="schedule-item">
            {item.data.map((content, idx) => (
              <p key={idx}>{content}</p>
            ))}
          </div>
        ))}
      </section>
      <section>
        <h2>第二天</h2>
        {day2.map(item => (
          <div key={item.id} className="schedule-item">
            {item.data.map((content, idx) => (
              <p key={idx}>{content}</p>
            ))}
          </div>
        ))}
      </section>
      <section>
        <h2>第三天</h2>
        {day3.map(item => (
          <div key={item.id} className="schedule-item">
            {item.data.map((content, idx) => (
              <p key={idx}>{content}</p>
            ))}
          </div>
        ))}
      </section>
    </div>
  );
}

关键修改点说明

  1. 添加缓存键:['agenda-schedule-data']是Next.js识别缓存条目的唯一标识,没有它unstable_cache无法正确管理缓存的更新和失效。
  2. 页面级revalidate:必须在页面组件顶部导出这个配置,和缓存函数的revalidate时间同步,确保页面静态内容和数据缓存一起每3000秒更新一次。
  3. 修复错误处理:移除原代码中catch块的await theData()递归调用,改成返回空数组,避免无限循环导致的内存溢出或缓存异常。
  4. 优化数据处理:用forEach替代map(因为不需要返回新数组),移除不必要的@ts-ignore和冗余类型定义,让代码更整洁。
  5. 环境变量处理:Google服务账号的私钥里的\\n要替换成真实换行符\n,否则JWT认证会失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 21:35:08