Deno 2 的定位变化

Deno 最初以“修正 Node 的历史错误”为卖点:默认安全、原生 TypeScript、用 URL 导入模块。但现实是 npm 生态太大了,绕开它等于自我孤立。

Deno 2 因此做了一个关键转向:向后兼容 Node 与 npm。现在你可以在 Deno 里直接 import express from "npm:express",甚至运行一个已有的 Node 项目。这让 Deno 从“另一个运行时”变成了“更安全的 Node 替代”。

权限模型:默认零信任

Deno 最独特的能力是细粒度权限,脚本默认拿不到任何系统访问权:

# 只允许读取当前目录、只允许访问一个域名
deno run --allow-read=./data --allow-net=api.example.com main.ts

# 完全不授权,用于运行不可信脚本
deno run --no-prompt untrusted.ts

# 交互式授权
deno run --prompt worker.ts

代码中也可以动态申请与撤销:

// 运行时申请权限
const status = await Deno.permissions.request({ name: "net", host: "api.example.com" });
if (status.state !== "granted") {
  throw new Error("需要网络权限");
}

// 用完立即撤销,缩小攻击面
await Deno.permissions.revoke({ name: "net" });

// 查询状态
console.log(await Deno.permissions.query({ name: "read", path: "/etc" }));

这套模型特别适合跑第三方插件、AI 生成的脚本、CI 里的临时工具。

原生 TypeScript 与内置工具链

deno init my-app        # 生成项目骨架
deno check main.ts      # 类型检查,无需 tsconfig
deno test               # 内置测试
deno lint               # 内置 lint
deno fmt                # 内置格式化
deno task dev           # 任务运行器
deno compile -o app main.ts   # 编译成单文件二进制

没有 prettier、eslint、ts-node、tsx、dotenv……全部内置,项目根目录只需要 deno.json:

{
  "tasks": { "dev": "deno run --watch --allow-net main.tsx" },
  "imports": {
    "@std/http": "jsr:@std/http@^1.0.0",
    "hono": "npm:hono@^4"
  },
  "compilerOptions": { "strict": true, "jsx": "react-jsx" },
  "nodeModulesDir": "auto"
}

实战:一个带权限控制的静态 API 服务

import { Hono } from "hono";

const app = new Hono();

app.get("/", (c) => c.text("deno + hono"));

app.get("/file/:name", async (c) => {
  const name = c.req.param("name");
  // Deno.readTextFile 需要 --allow-read 才能成功
  const text = await Deno.readTextFile(`./data/${name}.md`);
  return c.json({ name, length: text.length });
});

app.post("/webhook", async (c) => {
  const body = await c.req.json<{ event: string }>();
  const kv = await Deno.openKv(); // 内置 KV,零配置持久化
  await kv.set(["events", Date.now()], body);
  return c.json({ ok: true });
});

Deno.serve({ port: 8000, onListen: ({ port }) => console.log(port) }, app.fetch);

Deno.openKv() 是内置的键值存储,本地落盘、云端(Deno Deploy)自动分布式,写小型服务时省掉一个 Redis。

部署到边缘:Deno Deploy

# 安装 deployctl
deno install -gA jsr:@deno/deployctl

# 一键部署
deployctl deploy --project=my-api main.ts

Deno Deploy 把代码分发到全球多个边缘节点,冷启动极低,且天然继承 Deno 的权限沙箱。

Deno 还是 Node?

  • 选 Deno:需要安全沙箱、想要零配置 TS、部署到边缘、写一次性脚本或 CLI。
  • 留在 Node:重度依赖特定 C++ 扩展、团队已有成熟 Node 基建。

实际上两者差距正在缩小:Node 也在补 --experimental-permission 与原生 TS 支持。Deno 的优势更多在于“默认正确”的设计取舍。

小结

Deno 2 是一次务实的转身:保留了安全的默认、原生 TS 和现代标准库,同时拥抱 npm 生态。它现在是一个真正可以落地生产的选择,而不是一个理想主义实验。