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)
核心概念对照表¶
| Deno | Node.js 对应 | 改进 |
|---|---|---|
| deno run | node | 内置TS, 权限控制 |
| deno add | npm install | 支持JSR和npm |
| deno fmt | prettier | 内置, 零配置 |
| deno lint | eslint | 内置, 更快 |
| deno test | jest/vitest | 内置 |
| deno bench | benchmark.js | 内置基准测试 |
| deno doc | typedoc | 内置文档生成 |
| deno.json | package.json + tsconfig | 统一配置 |
| JSR | npm 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.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);
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 });
});
使用 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")}!`);
});
7. 与 Node.js 项目互操作¶
// 直接使用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 包?¶
参考资源¶
- Deno 官网 - 官方网站
- Deno 文档 - 完整文档
- Deno GitHub - 源代码
- JSR - JavaScript Registry
- Deno Deploy - 边缘部署平台
- Deno 标准库 - 官方标准库