引言
在将传统的 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 引擎之上,使用标准的fetch
API 处理 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 应用,同时掌握如何高效排查与解决各种环境差异带来的错误。祝部署顺利,开发愉快!