站点图标 高效码农

Express应用云端部署防踩坑指南:Cloudflare Workers与Vercel实战解析

引言

在将传统的 Node.js/Express.js 应用迁移到云端运行时,Cloudflare Workers 与 Vercel 是两种常见的 Serverless 选项。它们各自具备不同的环境约束与部署流程,稍有不慎就可能遇到运行时错误。本文将通过实战案例,带你一步步:

  1. Cloudflare Workers 上启动、适配并部署 Express 应用
  2. Vercel 平台上进行类似部署
  3. 系统地排查与解决部署过程中常见的各种错误

全文内容完全基于您提供的项目记录与日志,不做任何外部补充。希望这篇文章能帮助你快速上手并避开“坑”。


目录


一、Cloudflare Workers 上部署 Express 应用

小问:Cloudflare Workers 环境和标准 Node.js 有何不同?
:Workers 运行在 V8 引擎之上,使用标准的 fetch API 处理 HTTP 请求,不支持大部分 Node.js 核心模块(如 fshttp 等),且天生无状态。

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 需做以下调整:

  1. 路由改写:将 app.get()app.use() 等逻辑,转化为 Worker 的 fetch 事件处理
  2. 替代不兼容模块:如需文件操作,需借助外部 KV 存储或 Durable Objects
  3. 可选框架:可选用 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

  1. 将项目推送到 GitHub/GitLab
  2. 在 Vercel 仪表板点击 New Project
  3. 选择对应仓库,确认自动检测到 Node.js
  4. 设置环境变量(如有)
  5. 点击 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 可能是相对路径,或上传目录未提前创建

  • 解决

    1. 确保目录存在:

      fs.mkdir("/tmp/uploads", { recursive: true }, err => cb(err, "/tmp/uploads"));
      
    2. 验证 req.file.path 以绝对路径 /tmp/ 开头;否则抛错提示。

5. 原始字节序列响应

  • 现象:客户端收到形如 b'\x0c\x90…' 的二进制串

  • 原因:未正确设置 Content-Type、或返回值非字符串

  • 解决

    1. 确保使用 res.json()res.send(),让 Express 自动注入 Content-Type

    2. 在返回前打印日志:

      console.log("Converted HTML:", htmlContent);
      console.log("Type:", typeof htmlContent);
      
    3. 简化或排查 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 应用,同时掌握如何高效排查与解决各种环境差异带来的错误。祝部署顺利,开发愉快!

退出移动版