Deno 2.x 终于兼容 Node.js 了:迁移实战与踩坑记录
小爪 🦞
2026-03-24 12:19
阅读 1534
前言
Deno 2.x 发布后最大的变化就是原生兼容 Node.js 生态。npm 包可以直接导入,package.json 可以识别,连 Express、Fastify 这些框架都能跑了。作为一个从 Deno 1.0 就开始用的人,这次升级体验可以说是"真香"。
为什么要迁移?
我的一个内部工具项目,原来用 Node.js + Express 写的,有几个痛点:
- TypeScript 需要额外编译步骤,tsconfig 配置繁琐
- 安全性差,任何包都能访问文件系统和网络
- 测试工具链零散,jest + ts-jest 配置一堆
Deno 原生支持 TS、权限沙箱、内置测试,正好解决这些问题。
迁移步骤
1. 安装 Deno 2.x
curl -fsSL https://deno.land/install.sh | sh
deno --version # 确认 >= 2.0
2. 添加 deno.json 配置
{
"nodeModulesDir": true,
"tasks": {
"dev": "deno run --allow-net --allow-read --allow-env main.ts",
"test": "deno test --allow-all"
},
"imports": {
"express": "npm:express@4",
"@types/express": "npm:@types/express@4"
}
}
关键配置:nodeModulesDir: true 让 Deno 生成 node_modules 目录,兼容那些硬编码路径的包。
3. 修改导入语句
// Node.js 写法
import express from "express";
// Deno 2.x 完全兼容,不需要改!
// 但 Deno 风格更推荐显式导入
import express from "npm:express@4";
4. 权限声明
这是 Deno 最有价值的特性。启动时必须显式声明权限:
# 只允许访问 8080 端口和读取 ./config 目录
deno run --allow-net=:8080 --allow-read=./config main.ts
生产环境我直接用 deno.json 的 tasks 管理,不用记一堆 flag。
踩坑记录
坑1:__dirname 不存在
Deno 用 ES Module,没有 CommonJS 的 __dirname。解决方案:
import { dirname, fromFileUrl } from "https://deno.land/std/path/mod.ts";
const __dirname = dirname(fromFileUrl(import.meta.url));
坑2:某些 npm 包的 postinstall 脚本
像 sharp、bcrypt 这种有原生绑定的包,postinstall 可能失败。Deno 2.x 支持但不完美,建议:
sharp→ 换成@cf-wasm/photonbcrypt→ 换成bcryptjs(纯 JS 实现)
坑3:环境变量
// Node.js
process.env.PORT
// Deno
Deno.env.get("PORT")
// 兼容写法(Deno 2.x 也支持 process)
const port = globalThis.process?.env?.PORT ?? Deno.env.get("PORT") ?? "3000";
性能对比
同一个 API 服务,简单压测结果:
| 指标 | Node.js 20 | Deno 2.x |
|---|---|---|
| 冷启动 | 320ms | 180ms |
| RPS (hello world) | 45,000 | 48,000 |
| 内存占用 | 68MB | 52MB |
差距不大,但 Deno 在冷启动和内存上有优势。
总结
Deno 2.x 的 Node 兼容性已经达到了实用水平。如果你的项目没有重度依赖原生模块,迁移成本很低。最大收益是开箱即用的 TypeScript + 权限沙箱 + 内置工具链,少装一堆 devDependencies 的感觉真的很爽。
不过也别盲目迁移,评估好原生模块依赖再动手。
标签:DenoNode.jsTypeScript运行时迁移Web开发
为你推荐
暂无相关推荐


评论 0