跳转至

Deno 2 运行时

为什么要学

Deno 是 Node.js 创始人 Ryan Dahl 的"重做版"——修复了他认为 Node.js 的设计缺陷:

  • TypeScript 原生:直接运行 .ts 文件,零配置
  • 安全第一:默认无文件/网络/环境变量权限,需显式授予
  • 标准库:官方维护的标准库,不依赖第三方基础包
  • npm 兼容:Deno 2 完整支持 npm 包和 Node.js API
  • 内置工具:格式化、lint、测试、文档生成、打包全内置
  • Web 标准:fetch、WebSocket、Web Workers 等原生支持

Deno 2 是一个更安全、更现代、更简洁的 JavaScript/TypeScript 运行时。

核心概念

白话解释

  • 权限系统:Deno 默认"什么都不能做",你需要明确告诉它"可以读文件"、"可以访问网络"等
  • JSR:JavaScript Registry——Deno 的官方包注册表(比 npm 更现代)
  • deno.json:项目配置文件(替代 package.json + tsconfig.json)

核心概念对照表

DenoNode.js 对应改进
deno runnode内置TS, 权限控制
deno addnpm install支持JSR和npm
deno fmtprettier内置, 零配置
deno linteslint内置, 更快
deno testjest/vitest内置
deno benchbenchmark.js内置基准测试
deno doctypedoc内置文档生成
deno.jsonpackage.json + tsconfig统一配置
JSRnpm registry原生TS, 更安全
--allow-net(无限制)网络权限控制

安装配置

安装

# macOS/Linux
curl -fsSL https://deno.land/install.sh | sh

# Windows (PowerShell)
irm https://deno.land/install.ps1 | iex

# Homebrew
brew install deno

# 验证
deno --version

项目初始化

deno init my-project
cd my-project

# 目录结构:
# ├── deno.json
# ├── main.ts
# └── main_test.ts

deno.json 配置

{
  "tasks": {
    "dev": "deno run --watch main.ts",
    "start": "deno run --allow-net --allow-read main.ts",
    "test": "deno test"
  },
  "imports": {
    "@std/": "jsr:@std/",
    "oak": "jsr:@oak/oak@^17"
  },
  "compilerOptions": {
    "strict": true
  }
}

快速上手

Hello World

// main.ts
console.log("Hello from Deno!");

// 直接使用fetch(Web标准API)
const response = await fetch("https://api.github.com/users/denoland");
const data = await response.json();
console.log(data.name);
# 需要网络权限
deno run --allow-net main.ts

# 或允许所有权限(开发时)
deno run -A main.ts

HTTP 服务器

// server.ts
Deno.serve({ port: 8000 }, (req: Request): Response => {
  const url = new URL(req.url);

  if (url.pathname === "/") {
    return new Response("Hello Deno!", {
      headers: { "content-type": "text/plain" },
    });
  }

  if (url.pathname === "/api/time") {
    return Response.json({ time: new Date().toISOString() });
  }

  return new Response("Not Found", { status: 404 });
});
deno run --allow-net server.ts

使用 npm 包

// Deno 2 完全兼容npm
import express from "npm:express@4";
import chalk from "npm:chalk@5";

const app = express();

app.get("/", (req, res) => {
  console.log(chalk.green("收到请求"));
  res.json({ message: "Hello from Express on Deno!" });
});

app.listen(3000);

权限系统

# 精确控制权限
deno run --allow-net=api.example.com main.ts        # 只允许特定域名
deno run --allow-read=./data main.ts                 # 只允许读特定目录
deno run --allow-env=API_KEY,DATABASE_URL main.ts    # 只允许特定环境变量
deno run --allow-write=./output main.ts              # 只允许写特定目录

# 常用权限
# --allow-net     网络访问
# --allow-read    文件读取
# --allow-write   文件写入
# --allow-env     环境变量
# --allow-run     运行子进程
# --allow-ffi     外部函数接口

进阶用法

1. 标准库使用

// 使用JSR标准库
import { join } from "jsr:@std/path";
import { parse } from "jsr:@std/csv";
import { assertEquals } from "jsr:@std/assert";

// 文件操作
const content = await Deno.readTextFile("./data.csv");
const records = parse(content, { skipFirstRow: true });
console.log(records);

// 路径操作
const fullPath = join(Deno.cwd(), "src", "main.ts");

2. 测试

// main_test.ts
import { assertEquals, assertThrows } from "jsr:@std/assert";

Deno.test("基本加法", () => {
  assertEquals(1 + 1, 2);
});

Deno.test("异步测试", async () => {
  const response = await fetch("https://httpbin.org/get");
  assertEquals(response.status, 200);
});

Deno.test({
  name: "分组测试",
  fn: async (t) => {
    await t.step("步骤1", () => {
      assertEquals(true, true);
    });

    await t.step("步骤2", () => {
      assertThrows(() => { throw new Error("expected"); });
    });
  },
});
deno test                    # 运行所有测试
deno test --watch            # 监听模式
deno test --coverage=cov/    # 覆盖率
deno coverage cov/           # 查看覆盖率报告

3. Web 框架(Oak)

import { Application, Router } from "jsr:@oak/oak";

const router = new Router();

router.get("/", (ctx) => {
  ctx.response.body = "Hello Oak!";
});

router.get("/api/users/:id", (ctx) => {
  const id = ctx.params.id;
  ctx.response.body = { id, name: `User ${id}` };
});

router.post("/api/users", async (ctx) => {
  const body = await ctx.request.body.json();
  ctx.response.status = 201;
  ctx.response.body = { id: 1, ...body };
});

const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());

console.log("Server running on http://localhost:8000");
await app.listen({ port: 8000 });

4. 内置工具

# 格式化代码
deno fmt
deno fmt --check  # 只检查不修改

# Lint
deno lint

# 文档生成
deno doc main.ts

# 编译为单文件可执行
deno compile --allow-net --allow-read server.ts
# 生成: server (可直接运行的二进制文件)

# 基准测试
deno bench bench.ts

5. KV 存储(内置)

// Deno KV - 内置键值数据库
const kv = await Deno.openKv();

// 写入
await kv.set(["users", "user-001"], { name: "张三", age: 25 });
await kv.set(["users", "user-002"], { name: "李四", age: 30 });

// 读取
const entry = await kv.get(["users", "user-001"]);
console.log(entry.value);  // { name: "张三", age: 25 }

// 列表查询(前缀扫描)
const iter = kv.list({ prefix: ["users"] });
for await (const entry of iter) {
  console.log(entry.key, entry.value);
}

// 原子操作
await kv.atomic()
  .check({ key: ["users", "user-001"], versionstamp: entry.versionstamp })
  .set(["users", "user-001"], { name: "张三", age: 26 })
  .commit();

6. Deploy (边缘部署)

// 针对Deno Deploy优化的代码
// 自动全球分发,零配置部署

Deno.serve((req) => {
  return new Response(`Hello from ${Deno.env.get("DENO_REGION")}!`);
});
# 使用deployctl部署
deno install -A jsr:@deno/deployctl
deployctl deploy --project=my-app ./main.ts

7. 与 Node.js 项目互操作

// deno.json
{
  "nodeModulesDir": "auto",
  "tasks": {
    "start": "deno run -A main.ts"
  }
}
// 直接使用Node.js API
import { readFileSync } from "node:fs";
import { join } from "node:path";
import process from "node:process";

const content = readFileSync(join(process.cwd(), "package.json"), "utf8");
console.log(JSON.parse(content).name);

常见问题

Q1: Deno 2 vs Node.js, 该选哪个?

场景推荐
新项目,追求现代化Deno
大量现有Node.js生态依赖Node.js (或Deno 2兼容模式)
安全敏感的应用Deno
边缘/Serverless部署Deno (Deno Deploy)
企业既有团队Node.js

Q2: JSR 和 npm 的关系?

  • JSR 是 Deno 推出的新一代包注册表
  • 原生支持 TypeScript(发布 .ts 源码)
  • Deno 可以同时使用 JSR 和 npm 的包
  • JSR 包也可以在 Node.js 中使用

Q3: 性能对比?

Deno 2 在大部分场景与 Node.js 相当或略快,在某些 I/O 场景下可能慢一点。主要优势在开发体验而非纯性能。

Q4: 如何处理没有类型的 npm 包?

// 可以使用 @ts-ignore 或声明
// @ts-types="npm:@types/express"
import express from "npm:express";

参考资源