Deno 运行时入门与架构解析:从安装、第一个 Web 服务到 V8 + Rust + Tokio 分层设计
2026/9/7 7:00:15 网站建设 项目流程

Deno 运行时入门与架构解析:从安装、第一个 Web 服务到 V8 + Rust + Tokio 分层设计

【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno

Deno 是一个以安全默认值和良好开发者体验为核心的 JavaScript、TypeScript 与 WebAssembly 运行时,构建在 V8、Rust 和 Tokio 之上。本文以仓库根目录 README 为主线,覆盖安装、第一个Deno.serve服务、权限模型等实战内容,并结合 doc/architecture.md 等仓库文档与源码,解释deno run --allow-net server.ts背后逐层工作的实现原理。

一、Deno 是什么

README 对 Deno 的定义非常直接:它是一个 JavaScript、TypeScript 和 WebAssembly 运行时(/ˈdiːnoʊ/,读作 “dee-no”),特性上强调两点:

  • 安全默认值(secure defaults):程序默认不能读取文件、访问网络、执行子进程,必须显式通过--allow-*参数或运行时权限询问授予能力;
  • 基于三大技术栈:V8(JavaScript 引擎)、Rust(系统语言)、Tokio(异步运行时)。

当前仓库的版本号为2.9.6,记录在 cli/lib/version.txt。

二、安装 Deno

README 给出了五种官方安装途径,覆盖 macOS、Linux 与 Windows。以下命令均可直接复制运行:

Shell(Mac、Linux):

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

PowerShell(Windows):

irm https://deno.land/install.ps1 | iex

Homebrew(Mac):

brew install deno

Chocolatey(Windows):

choco install deno

WinGet(Windows):

winget install --id=DenoLand.Deno

Scoop(Windows):

scoop install main/deno

README 同时说明,以上只是部分途径,完整安装方式列表见 Deno 官方文档。若希望从源码构建安装,则参考贡献指南(.github/CONTRIBUTING.md 中的 “Building from source” 一节)。构建所需的前提是理解本仓库的工作区结构——根 Cargo.toml 定义了一个包含cliruntime、数十个ext/*libs/*成员的 Cargo workspace,这正是下文分层架构在工程上的对应物。

三、你的第一个 Deno 程序:用Deno.serve写一个 Web 服务

README 指出,Deno 最典型的用途是构建 Web 服务器。完整步骤如下:

创建文件server.ts

Deno.serve((_req: Request) => { return new Response("Hello, world!"); });

然后运行:

deno run --allow-net server.ts

启动后,本地 Web 服务默认监听http://localhost:8000,访问该地址即可看到Hello, world!

3.1 为什么需要--allow-net

这里的权限参数不是装饰,而是 Deno 安全模型的入口:

  • --allow-net授予程序打开网络监听/连接的许可。不带该参数运行同一文件时,Deno.serve会在权限检查处报错,而不是默认放行。
  • 该 flag 的解析逻辑在 cli/args/flags.rs 中,可以看到--allow-net支持精确到地址的白名单形式(如--allow-net=8000--allow-net=127.0.0.1:8000),源码中会将其拼接为--allow-net=<allowlist>参数。
  • 权限的实际执行发生在 Rust 侧的 op 边界。doc/architecture.md 明确指出:“权限在 Rust 中、在 op 边界处检查,绝不在 JavaScript 中检查”,且“未授予的能力会让该 op 在执行任何工作之前就报错”。对应的权限模型代码位于 runtime/permissions/ 目录。

3.2Deno.serve的默认端口 8000 从哪里来

Deno.serve的实现位于 ext/http/00_serve.ts。其中:

  • 函数入口serve(arg1, arg2)支持两种签名:直接传 handler 函数,或传{ handler, port, hostname, ... }选项对象(见 ext/http/00_serve.ts);
  • README 中省略 port 时服务落在 8000 端口,对应源码中的默认值port: options.port ?? 8000(见 ext/http/00_serve.ts)。

也就是说,README 示例中的http://localhost:8000并非文档随口写的地址,而是Deno.serve内置的默认监听端口。

四、架构总览:README 背后的五层堆叠

README 本身是面向用户的最小文档,而 doc/architecture.md 给出了运行时本身的分层设计,这是理解“deno run一条命令如何工作”的关键。原文档中的层叠结构如下(自顶向下,每层只依赖其下的层):

+-----------------------------------------------------------+ | cli/ the `deno` binary: subcommands, tooling | +-----------------------------------------------------------+ | runtime/ deno_runtime: assembles the JS runtime | +-----------------------------------------------------------+ | ext/* extensions: native capabilities for JS | +-----------------------------------------------------------+ | libs/* deno_core + supporting crates (V8 bridge) | +-----------------------------------------------------------+ | V8 + Tokio JavaScript engine and async runtime | +-----------------------------------------------------------+

4.1 CLI 层(cli/):用户直接触碰的一切

denocrate 拥有 flag 解析、所有子命令(runtestfmtlintcompilebundleinstallpublish等)、包管理工具、LSP,以及把模块解析与运行时连接起来的 module loader。关键入口文件:

文件职责
cli/main.rs进程入口与命令路由
cli/args/flags.rs完整的clapflag 与子命令定义,新增 flag 或子命令从这里开始
cli/tools/<tool>/每个子命令一个模块(简单命令如 cli/tools/fmt.rs,复杂命令如cli/tools/test/目录)
cli/module_loader.rs解析并加载模块,把 resolver 与 module graph 桥接到运行时

架构文档强调 CLI 层是“刻意沉重”的:它引入 TypeScript 类型检查、npm 与 JSR 解析、lockfile、bundler 等能力,而更低的层不允许反向依赖它。

4.2 运行时层(runtime/):deno_runtimecrate

这一层把deno_core加上一组精选的 extensions 组装成一个可工作的 JavaScript 运行时,是希望嵌入“Deno 运行时”而非“Deno CLI”的外部项目使用的部分。关键文件:

  • runtime/worker.rs —— 构造主 worker:isolate、op 集合、bootstrap 序列;
  • runtime/web_worker.rs —— Web Worker 变体;
  • runtime/permissions/ —— 权限模型,门控所有敏感 op(read、write、net、env、run、ffi、sys)。

4.3 扩展层(ext/*):平台能力的真正所在

ext/下每个目录都是一个自包含的 extension:一个 Rust crate,定义ops(可从 JS 调用的原生函数),外加在其上暴露高层 API 的 JavaScript 模块。Web 平台能力(ext/webext/fetchext/cryptoext/webgpu等)、系统访问(ext/fsext/netext/process等)、Deno 独有特性(ext/kvext/cronext/ffiext/napi)以及 Node 兼容层(ext/node/ 承载大部分node:*内建模块,另有ext/node_cryptoext/node_sqlite)都分布在这里。

一个 extension 的典型形态是三步:

  1. Rust 侧用#[op2]函数完成特权操作,需要时附带权限检查;
  2. 00_*.js/01_*.js等编号 JS 模块构建公开 API,通过Deno.core.ops调用这些 op——例如本文第三节的Deno.serve就位于 ext/http/00_serve.ts,同层的原生 op 定义在 ext/http/lib.rs;
  3. 在 runtime/worker.rs(以及 CLI 的 snapshot 构建)中注册该 extension,使其成为组装后运行时的一部分。

架构文档还给出了一条明确的扩展守则:新增原生功能时,应在对应的ext/<name>/crate 中添加 op,而不是伸手去改 runtime 或 CLI。

4.4 核心层(libs/*):Rust 与 V8 之间的桥

libs/保存deno_core及支撑 crate,拥有 op 基础设施、module loader trait、snapshot 机制、JsRuntime 事件循环,以及serde_v8序列化层。值得注意的成员:

  • libs/core/ ——deno_core本身:JsRuntime、op 注册、module map、inspector 集成;
  • libs/ops/ —— 生成 Rust/V8 胶合代码的#[op2]过程宏;
  • libs/serde_v8/ —— Rust 类型与 V8 值之间接近零拷贝的序列化;
  • libs/resolver/、libs/npm/、libs/npm_installer/、libs/lockfile/、libs/config/ 等 —— CLI 组合使用的模块解析与包管理积木。

这些 crate 刻意不含任何 CLI 关注点,以便独立单测并被其他工具复用。

4.5 横切概念:Ops、Extensions、Workers、Resources、Permissions

架构文档归纳了五个贯穿全栈的概念:

  • Ops是 JavaScript 触及原生代码的唯一途径。同步 op 立即返回,异步 op 返回一个在未来事件循环上解决的 future;
  • Extensions把 op 与其 JavaScript 打包在一起,是运行时组合的基本单元;
  • Workers是隔离的 JS 执行上下文(主 worker 与 Web Worker),各自拥有独立的 V8 isolate;
  • Resources是被管理的句柄(打开的文件、socket、reader),由deno_core跟踪,以整型 id 跨 Rust/JS 边界传递;
  • Permissions在 Rust 侧的 op 边界强制执行,未授予的能力会让 op 在任何工作发生前就报错。

五、定位你的改动:架构文档中的速查表

doc/architecture.md 末尾给出了一张“我要做 X,应该从哪开始”的表,对理解各模块职责边界非常有用:

需求起始位置
添加或修改 CLI flag / 子命令cli/args/flags.rscli/tools/
给 JS 增加原生能力ext/<name>/(op + JS)
修改运行时的组装方式runtime/worker.rs
触碰 Rust/V8 桥或 op 宏libs/corelibs/ops
修改模块 / npm / JSR 解析libs/resolverlibs/npm、CLI

更细粒度的目录地图见 doc/codebase-map.md,各层的测试方式见 doc/testing.md,此外 doc/ci.md 解释了 CI 工作流如何生成、以及纯文档改动(仅触碰doc/的 PR)为何只需跑lint任务。

六、延伸阅读资源

README 列出的官方资源与本文相互印证:

  • Deno Docs:运行时、部署等的官方指南与参考文档;
  • Deno Standard Library@std):官方维护的通用工具库;
  • JSR:面向现代 JavaScript/TypeScript 的开源包注册表,其解析逻辑在仓库中对应cli/jsr.jslibs/resolver等模块;
  • 贡献指南:见 .github/CONTRIBUTING.md,涵盖从源码构建的完整步骤。

七、小结

从仓库视角看,README 中“安装 → 写server.tsdeno run --allow-net server.ts→ 访问localhost:8000”这条最短路径,实际上穿过了整条技术栈:cli/args/flags.rs解析出--allow-netcli/的 module loader 拉取并解析server.tsruntime/worker.rs组装 isolate 与 op 集合,ext/http提供Deno.serve(默认端口 8000),libs/corelibs/ops承担 Rust/V8 桥接,而 net 权限在 op 边界完成裁决。理解这条调用链,也就理解了 Deno“安全默认 + 分层可复用”这两大设计目标的工程落地方式。

【免费下载链接】denoA modern runtime for JavaScript and TypeScript.项目地址: https://gitcode.com/GitHub_Trending/de/deno

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询