引言
在将传统的 Node.js/Express.js 应用迁移到云端运行时,Cloudflare Workers 与 Vercel 是两种常见的 Serverless 选项。它们各自具备不同的环境约束与部署流程,稍有不慎就可能遇到运行时错误。本文将通过实战案例,带你一步步:
- 
在 Cloudflare Workers 上启动、适配并部署 Express 应用 
- 
在 Vercel 平台上进行类似部署 
- 
系统地排查与解决部署过程中常见的各种错误 
全文内容完全基于您提供的项目记录与日志,不做任何外部补充。希望这篇文章能帮助你快速上手并避开“坑”。
目录
- 
一、Cloudflare Workers 上部署 Express 应用 
- 
二、Vercel 上部署 Express 应用 
- 
三、常见部署与运行时错误排查 
- 
四、FAQ 
- 
附录:HowTo & FAQ Schema (JSON-LD) 
一、Cloudflare Workers 上部署 Express 应用
小问:Cloudflare Workers 环境和标准 Node.js 有何不同?
答:Workers 运行在 V8 引擎之上,使用标准的fetchAPI 处理 HTTP 请求,不支持大部分 Node.js 核心模块(如fs、http等),且天生无状态。
1. 安装 Wrangler CLI
Wrangler 是 Cloudflare 官方提供的命令行工具,用来初始化、开发和部署 Workers 项目。
npm install -g wrangler
2. 创建 Worker 项目
npx wrangler init my-worker
cd my-worker
- 
my-worker:可自定义名称
- 
会生成基本项目结构,包括 wrangler.toml与示例index.js
3. 适配现有 Express 应用
Express 的中间件、路由与 Node.js API 需做以下调整:
- 
路由改写:将 app.get()、app.use()等逻辑,转化为 Worker 的fetch事件处理
- 
替代不兼容模块:如需文件操作,需借助外部 KV 存储或 Durable Objects 
- 
可选框架:可选用 Hono 等社区库,提供类似 Express 的路由/中间件体验 
4. 配置 wrangler.toml
name = "my-worker"
type = "javascript"
[build]
command = "npm run build"      # 如果有打包步骤
upload_format = "service-worker"
node_compat = true             # 启用部分 Node.js API 兼容
- 
node_compat = true:有助于少量标准模块运行
5. 编写 Worker 入口代码
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    // 将路由逻辑写在这里
    if (url.pathname === "/") {
      return new Response("Hello from Cloudflare Worker!");
    }
    // 其余路由...
    return new Response("Not Found", { status: 404 });
  },
};
6. 本地开发与测试
wrangler dev
# 本地启动在 http://localhost:8787
7. 部署到 Cloudflare
wrangler publish
表格:Cloudflare Workers vs. 传统 Node.js 环境
| 特性 | Cloudflare Workers | 传统 Node.js | 
|---|---|---|
| 文件系统 | 只读(无 /tmp) | 可读写 | 
| 核心模块 | 仅限少量兼容模块 | 完整支持 | 
| 进程模型 | 无状态、事件触发 | 持久化进程 | 
| 冷启动 | 极快 | 视应用而定 | 
二、Vercel 上部署 Express 应用
小问:为什么在 Vercel 上更容易部署?
答:Vercel 原生支持 Node.js,将 Express 导出的app实例直接转换为 Serverless Function。
1. 准备 Express 应用
在主文件(如 index.js)中:
import express from 'express';
const app = express();
// 路由示例
app.get("/", (req, res) => {
  res.send("Hello from Express on Vercel!");
});
// 仅在本地开发时启动监听
if (process.env.NODE_ENV !== "production" && !process.env.VERCEL) {
  const PORT = process.env.PORT || 3000;
  app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
}
export default app;  // 关键:导出 app 实例
2. 创建 vercel.json(可选)
{
  "version": 2,
  "builds": [
    { "src": "index.js", "use": "@vercel/node" }
  ],
  "routes": [
    { "src": "/(.*)", "dest": "/index.js" }
  ]
}
- 
指定入口与路由规则 
3. 连接 Git 并导入到 Vercel
- 
将项目推送到 GitHub/GitLab 
- 
在 Vercel 仪表板点击 New Project 
- 
选择对应仓库,确认自动检测到 Node.js 
- 
设置环境变量(如有) 
- 
点击 Deploy 
部署完成后,每次推送到指定分支都会触发自动 CI/CD。
4. 使用 Vercel CLI(可选)
npm install -g vercel
vercel        # 首次执行并链接项目
vercel --prod # 生产部署
三、常见部署与运行时错误排查
下面基于实战日志,整理了数个典型错误的成因与解决方案。
1. ERR_MODULE_NOT_FOUND:大小写敏感问题
- 
现象:日志提示找不到 /var/task/themes/markdown/Notion.js
- 
原因:本地 Windows 不区分大小写,Vercel Linux 区分。文件实际名为 notion.js。
- 
解决:在 themes/index.js中将import notion from './markdown/Notion.js';修改为 import notion from './markdown/notion.js';
2. EROFS: read-only file system
- 
现象: mkdir '/var/task/uploads'失败
- 
原因:Serverless 函数的根目录只读,只有 /tmp可写
- 
解决:将 Multer 存储目录配置为 /tmp/uploads,例如:const storage = multer.diskStorage({ destination(req, file, cb) { cb(null, "/tmp/uploads"); }, filename(req, file, cb) { cb(null, file.fieldname + Date.now() + path.extname(file.originalname)); } });
3. ReferenceError: path is not defined
- 
现象:在使用 path.extname()时抛出未定义错误
- 
原因:遗漏导入内置模块 path
- 
解决:在文件顶部添加: import path from 'path';
4. ENOENT: no such file or directory, open ‘tmp/uploads/…’
- 
现象: readFile找不到文件
- 
原因:Multer 提供的 req.file.path可能是相对路径,或上传目录未提前创建
- 
解决: - 
确保目录存在: fs.mkdir("/tmp/uploads", { recursive: true }, err => cb(err, "/tmp/uploads"));
- 
验证 req.file.path以绝对路径/tmp/开头;否则抛错提示。
 
- 
5. 原始字节序列响应
- 
现象:客户端收到形如 b'\x0c\x90…'的二进制串
- 
原因:未正确设置 Content-Type、或返回值非字符串
- 
解决: - 
确保使用 res.json()或res.send(),让 Express 自动注入Content-Type。
- 
在返回前打印日志: console.log("Converted HTML:", htmlContent); console.log("Type:", typeof htmlContent);
- 
简化或排查 convertMarkdown,确保其返回的是纯文本/HTML 字符串。
 
- 
四、FAQ
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Cloudflare Workers 与普通 Node.js 有何区别?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Workers 运行在 V8 引擎中,使用 fetch API 处理请求,只有只读文件系统,不支持大多数 Node.js 核心模块。"
      }
    },
    {
      "@type": "Question",
      "name": "为什么要将 Multer 的存储目录改为 /tmp?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Serverless 环境下,根目录只读,唯一可写目录是 /tmp,用于短期存储。"
      }
    },
    {
      "@type": "Question",
      "name": "Vercel 部署时如何避免大小写文件名错误?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "在导入文件时严格保持与磁盘上一致的大小写,比如将 Notion.js 改为 notion.js。"
      }
    }
  ]
}
</script>
常见提问(答案隐藏,可点击展开)
- 
如何安装 Wrangler CLI? 
 使用npm install -g wrangler。
- 
Vercel 如何自动部署? 
 将项目连接到 Git 仓库,每次推送都会触发构建。
- 
Multer 文件未找到怎么办? 
 检查上传目录是否存在,并确保使用绝对路径。
附录:HowTo & FAQ Schema (JSON-LD)
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "在 Vercel 上部署 Express.js 应用",
  "description": "演示如何准备代码、配置 vercel.json 并完成部署的完整流程。",
  "step": [
    {
      "@type": "HowToStep",
      "name": "准备 Express 应用并导出 app",
      "text": "在 index.js 中通过 export default app; 导出 Express 实例。"
    },
    {
      "@type": "HowToStep",
      "name": "创建 vercel.json",
      "text": "在根目录中添加 vercel.json,指定 builds 与 routes。"
    },
    {
      "@type": "HowToStep",
      "name": "连接 Git 并部署",
      "text": "将项目推送至 Git 仓库,在 Vercel 仪表板导入并点击 Deploy。"
    }
  ]
}
</script>
通过上述内容,你可以在两大主流 Serverless 平台快速部署并稳定运行 Express 应用,同时掌握如何高效排查与解决各种环境差异带来的错误。祝部署顺利,开发愉快!
