Deno 源码解析:Ryan Dahl 用 Rust 重新定义 JavaScript 运行时
引言
Deno 是 Node.js 创始人 Ryan Dahl 的”第二次尝试”。他在 2018 年的演讲中列举了 Node.js 的设计遗憾,然后用 Rust 从零构建了 Deno。核心改进:默认安全(沙箱权限)、原生 TypeScript 支持、基于 URL 的模块系统。
项目背景
Deno 使用 Rust 编写核心运行时,V8 作为 JavaScript 引擎,Tokio 作为异步运行时。2024 年推出的 Deno 2.0 增加了 Node.js 兼容层,大幅降低了迁移成本。
核心源码解析
1. 权限系统
Deno 最大的创新是默认拒绝所有权限:
pub struct Permissions {
pub read: UnaryPermission<ReadDescriptor>,
pub write: UnaryPermission<WriteDescriptor>,
pub net: UnaryPermission<NetDescriptor>,
pub env: UnaryPermission<EnvDescriptor>,
pub run: UnaryPermission<RunDescriptor>,
pub ffi: UnaryPermission<FfiDescriptor>,
}
impl Permissions {
pub fn check_read(&self, path: &Path) -> Result<(), AnyError> {
self.read.check(path)
}
}
每次文件读写、网络请求、环境变量访问都需要显式授权(--allow-read、--allow-net 等)。这在运行时层面实现了最小权限原则。
2. Op 系统(运行时扩展)
Deno 通过 “Op” 机制将 Rust 函数暴露给 JavaScript:
#[op]
async fn op_read_file(state: &mut OpState, path: String) -> Result<Vec<u8>, AnyError> {
let permissions = state.borrow::<Permissions>();
permissions.check_read(&PathBuf::from(&path))?;
tokio::fs::read(path).await.map_err(Into::into)
}
#[op] 宏自动生成 V8 绑定代码,开发者只需要写 Rust 函数,框架负责 JS↔Rust 的桥接。3. 模块加载器
基于 URL 的模块系统,支持远程导入:
pub struct ModuleLoader {
pub async fn load(specifier: &ModuleSpecifier) -> Result<ModuleSource, AnyError> {
match specifier.scheme() {
"file" => self.load_from_disk(specifier).await,
"https" | "http" => self.load_from_network(specifier).await,
_ => Err(anyhow!("Unsupported scheme")),
}
}
}
远程模块会被缓存到本地,后续加载直接使用缓存,兼顾了便利性和性能。
关键设计模式
#[op] 宏大幅简化了 Rust↔JS 的互操作。 3. URL 即模块:消除了 node_modules 的复杂性。总结
Deno 是对 Node.js 设计缺陷的系统性修正。权限沙箱、Op 系统、URL 模块加载,每个设计决策都有明确的动机。对于关注运行时安全和 Rust+V8 集成的开发者,Deno 的源码是必读材料。
夜雨聆风