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

Express临时存储数据返回功能在Vercel部署后失效如何解决?

问题原因及解决方案

核心原因

Vercel 采用无服务器(Serverless)函数架构,和本地长期运行的 Express 服务器逻辑完全不同:

  1. 你代码里app.listen()中的初始化逻辑不会在 Vercel 环境执行——Vercel 不会让应用持续监听端口,而是将 Express 包装成按需触发的 serverless 函数,只有请求到来时才启动实例处理。
  2. 即使某实例执行了缓存初始化,请求结束后实例可能被销毁,下次请求会启动全新实例,内存缓存无法跨请求/实例共享,自然返回空对象{}。

解决方案

方案一:请求触发时初始化内存缓存(低成本,适合低频更新场景)

将缓存初始化逻辑从app.listen()移到请求中间件中,确保每个实例启动时都会初始化缓存,同一实例内的请求可复用缓存:

修改index.js:

import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import { routes } from '../routes';

export let cache = {};
let isCacheInitialized = false;

// 封装缓存初始化逻辑
const initCache = async () => {
  if (!isCacheInitialized) {
    const result = await someFunc(); // 如果someFunc是同步函数,去掉await
    cache = result;
    isCacheInitialized = true;
  }
};

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
app.use(cors());
app.use(express.json());
app.use(morgan('tiny'));

// 在路由处理前初始化缓存
app.use(async (req, res, next) => {
  await initCache();
  next();
});

app.use('/', routes);
app.disable('x-powered-by');

// Vercel 需要导出 app,而非调用 listen
export default app;

router.js保持原有逻辑即可。

注意:冷启动请求会触发初始化,后续同一实例的请求复用缓存;若实例被销毁,下次请求会重新初始化,适合数据更新不频繁的场景。

方案二:使用外部缓存存储(高可靠,适合跨实例共享场景)

用 Redis 替代内存缓存,Vercel 官方提供 Redis 集成,可实现跨实例的缓存共享与持久化:

  1. 安装 Redis 客户端:npm install ioredis
  2. 在 Vercel 控制台启用 Redis 服务,获取REDIS_URL环境变量
  3. 修改代码:

index.js:

import express from 'express';
import cors from 'cors';
import morgan from 'morgan';
import { routes } from '../routes';
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL);

// 封装缓存读取/初始化逻辑
export const getCache = async () => {
  const cachedStr = await redis.get('app-cache');
  if (cachedStr) {
    return JSON.parse(cachedStr);
  }
  // 无缓存时重新获取数据
  const result = await someFunc();
  // 设置缓存过期时间(示例为1小时)
  await redis.set('app-cache', JSON.stringify(result), 'EX', 3600);
  return result;
};

const app = express();
// 中间件部分不变

app.use('/', routes);
app.disable('x-powered-by');

export default app;

router.js:

import { getCache } from '../index.js';

routes.get(
  '/cache',
  catchAsync(async (req: Request, res: Response) => {
    const cache = await getCache();
    const query = req.query?.fields || '';
    const fields = query ? String(query).split(',') : [];
    res.status(200).json(fields.length ? pick(cache, fields) : cache);
  }),
);

方案三:构建阶段生成静态缓存文件(高性能,适合静态数据场景)

如果someFunc()的数据在构建阶段就能确定,可在 Vercel 构建时生成静态 JSON 文件,路由直接读取:

  1. 在package.json中添加构建脚本:
{
  "scripts": {
    "vercel-build": "node scripts/generate-cache.js"
  }
}
  1. 创建scripts/generate-cache.js:
const fs = require('fs');
const someFunc = require('../path/to/someFunc');

async function generateCache() {
  const result = await someFunc();
  fs.writeFileSync('./public/cache.json', JSON.stringify(result));
}

generateCache();
  1. 修改router.js读取静态文件:
import fs from 'fs/promises';
import path from 'path';

routes.get(
  '/cache',
  catchAsync(async (req: Request, res: Response) => {
    const cachePath = path.join(process.cwd(), 'public', 'cache.json');
    const cacheStr = await fs.readFile(cachePath, 'utf8');
    const cache = JSON.parse(cacheStr);
    const query = req.query?.fields || '';
    const fields = query ? String(query).split(',') : [];
    res.status(200).json(fields.length ? pick(cache, fields) : cache);
  }),
);

场景选择

  • 方案一:数据更新频率低、可接受冷启动延迟,优先选
  • 方案二:需要跨实例共享缓存、数据需定期更新,选这个
  • 方案三:数据在构建时固定不变,性能最优

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:40:31