Deno.js 入门完全指南:构建下一代 JavaScript 与 TypeScript 运行时

在现代 Web 开发领域,JavaScript 已经无处不在。 然而,作为开发者,你是否也曾感到过现有的工具在某些方面显得力不从心?当我们使用 Node.js 构建后端应用时,是否曾为复杂的 node_modules 目录、缺乏默认的安全机制以及在 TypeScript 和 JavaScript 之间切换的繁琐而感到困扰?

如果你对上述问题的回答是肯定的,那么你并不孤单。事实上,Node.js 的创造者 Ryan Dahl 本人早在 2018 年的一场著名演讲中,就深刻反思了 Node.js 设计上的缺陷。为了解决这些历史遗留问题,并利用现代技术栈打造一个更完美的工具,Deno 诞生了。

在这篇文章中,我们将深入探讨 Deno.js 这一新兴的 JavaScript 和 TypeScript 运行时。我们将一起了解它的核心架构、它与 Node.js 的本质区别,以及它如何通过“默认安全”、“去中心化包管理”等特性改变我们的开发习惯。我们还会通过实际的代码示例,展示如何使用 Deno 编写高效、安全的现代 Web 应用。让我们开始这段探索之旅吧!

Deno 的核心架构与技术基石

在深入了解特性之前,我们先来拆解一下 Deno 的底层构造。Deno 并不仅仅是一个简单的脚本运行器,它是一个精心设计的系统工程。

你可能知道,Node.js 也是基于 Google 的 V8 JavaScript 引擎构建的。Deno 延续了这一选择,因为它提供了目前业界最快的 JavaScript 执行速度。但 Deno 的独特之处在于它的底层系统语言选择——Rust

为什么选择 Rust?

相比于 Node.js 所使用的 C++,Rust 提供了更强的内存安全性。在 Deno 中,底层的控制逻辑、事件循环以及关键的绑定层都是由 Rust 编写的。这意味着我们在编写应用时,不太可能遇到因为底层内存错误导致的崩溃。此外,Deno 使用了 Tokio,这是 Rust 生态中著名的异步运行时。正是基于 Tokio 的高性能事件循环,Deno 能够以极低的资源消耗处理高并发的 I/O 操作。

值得一提的是,Deno 在 2020 年 5 月 13 日正式发布了其 1.0.0 版本。这意味着它已经足够稳定,可以投入生产环境使用。它不仅是一个运行时,更是一个完整的工具链,旨在为现代开发者提供便捷、高效的开发体验。

一、默认安全:沙箱机制与权限管理

Deno 最具革命性的特性之一,就是它的 “默认安全” 机制。这与传统的 Node.js 有着天壤之别。

在 Node.js 中,当你运行一个脚本(例如 node server.js)时,该脚本拥有与你当前用户完全相同的系统权限。它可以随意读取你的私人文件、修改系统配置、甚至安装恶意软件。这对于不可信的代码来说,是一个巨大的安全隐患。

Deno 则采用了与 Web 浏览器 类似的沙箱模型。除非你明确授权,否则 Deno 脚本无法访问你的文件系统、网络、环境变量或子进程。

让我们来看一个实际的例子。

假设我们要编写一个简单的脚本来读取系统中的文件。在 Deno 中,代码如下所示:

// file_reader.ts
const text = await Deno.readTextFile("./hello.txt");
console.log(text);
console.log("文件读取成功!");

如果我们尝试直接运行这段代码:

deno run file_reader.ts

将会发生什么?

Deno 会直接抛出一个错误,并终止程序:

error: Requires read access to "./hello.txt", run again with the --allow-read flag

这正是 Deno 的安全保护机制在起作用。为了允许这个程序读取文件,我们必须显式地添加权限标志

deno run --allow-read file_reader.ts

通过这种方式,我们始终清楚代码对系统的访问级别。这在处理从网络下载的第三方脚本时尤为重要。

常用的权限标志清单:

为了方便开发,Deno 提供了一套细粒度的权限控制标志:

  • INLINECODE9e3d7476: 允许读取文件系统。如果你只希望授权特定目录,可以这样写:INLINECODE097acfb9。
  • --allow-write: 允许写入文件系统。同样,你可以指定具体的路径。
  • INLINECODE5765298c: 允许网络访问(如 HTTP 请求)。例如:INLINECODEf2680937 仅允许访问 Google,其他域名将被阻止。
  • --allow-env: 允许读取和设置环境变量。
  • --allow-run: 允许运行子进程(如运行 shell 脚本)。
  • --allow-hrtime: 允许高精度时间测量(通常用于性能测试,但这可能会泄露某些计时攻击的指纹信息)。
  • --allow-plugin: 允许加载插件(注意:插件 API 目前已不太推荐使用,更多地被 WASM 取代)。
  • INLINECODE7bf7a4e7 或 INLINECODEdcb47857: 允许所有权限(仅在完全信任代码时使用)。

二、开箱即用的 TypeScript 支持

作为现代开发者,我们深知 TypeScript 带来的类型安全优势能极大地减少 Bug 并提高代码可维护性。然而,在 Node.js 环境中使用 TypeScript 往往需要繁琐的配置:我们需要安装 INLINECODE97a5c10d、配置 INLINECODE966e0a6f、设置构建脚本,甚至需要像 ts-node 这样的工具来运行开发环境。

Deno 彻底改变了这一现状。

Deno 内部直接集成了 TypeScript 编译器。这意味着 你无需任何配置,即可直接运行 .ts 文件。你甚至不需要在项目中安装 TypeScript 依赖。Deno 会在运行时自动将代码编译为 V8 可执行的字节码。

让我们体验一下这种“丝滑”的感觉。

创建一个名为 interface.ts 的文件,直接编写带有类型的代码:

// 定义一个用户接口
interface User {
  id: number;
  username: string;
  email: string;
}

// 创建一个处理用户的函数
function createUser(user: User): User {
  console.log(`正在创建用户: ${user.username}`);
  return user;
}

// 测试代码
const newUser: User = {
  id: 1,
  username: "deno_fan",
  email: "[email protected]"
};

createUser(newUser);

现在,直接运行它:

deno run interface.ts

Deno 会立即处理类型检查并执行代码。如果你尝试传入一个错误的属性,例如把 id 改为字符串,Deno 会在运行前就报错提示你。这种即时反馈极大地提高了我们的开发效率。

三、单一可执行文件与去中心化包管理

如果你曾经维护过复杂的 Node.js 项目,你一定对 INLINECODE4f306ebf 目录的大小感到震惊。安装几个依赖包,往往会生成成百上千个文件夹。此外,INLINECODE3ff39d03 和 package-lock.json 的冲突也是团队协作中常见的问题。

Deno 提出了一种 “去中心化” 的解决方案:

  • 没有 node_modules:Deno 不使用集中式的包目录。
  • 没有 package.json:Deno 不依赖元数据文件来管理依赖。
  • 通过 URL 导入模块:这是 Deno 最具争议但也最优雅的特性之一。我们可以直接通过源代码的 URL 来导入模块。

如何在 Deno 中引入依赖?

假设我们想要使用一个流行的标准库工具。我们不需要运行 INLINECODEa6c39091,只需要在代码顶部使用 INLINECODE0b078615 语句即可:

// 通过 URL 直接导入标准库模块
import { serve } from "https://deno.land/[email protected]/http/server.ts";

console.log("正在启动 HTTP 服务器...");

// 监听端口 8000
await serve((req) => {
  return new Response("Hello Deno!
");
}, { port: 8000 });

当你运行这段代码时,Deno 会自动下载、编译并缓存该模块。下次再运行时,它会直接使用本地缓存,速度非常快。

这种设计的巨大优势:

  • 全局共享缓存:无论你有多少个项目,只要使用了同一个版本的 URL,Deno 只会在系统里存储一份副本。这节省了大量的磁盘空间。
  • 单一可执行文件:Deno 的发布形式是一个单纯的二进制可执行文件,不依赖任何外部系统库。这使得分发 Deno 应用变得非常简单,只需复制一个文件即可。

代码解析:

  • 我们直接从 deno.land(Deno 的官方托管服务)导入了 HTTP 模块。
  • INLINECODE8943e236 指定了版本号。这保证了代码的稳定性——即使以后 INLINECODEf43382fc 库更新了,你的代码依然会使用 0.155.0 版本运行,不会出现“依赖地狱”问题。

四、现代浏览器兼容性与 ES 模块

Deno 的目标是保持与 Web 标准的一致性。它遵循 ES 模块 规范,不再使用 CommonJS(INLINECODE62f58616 语法)。这意味着我们必须使用 INLINECODEe8afa412 和 export 关键字。

更重要的是,Deno 原生支持 Fetch API。在 Node.js 中,如果你不想使用第三方库(如 INLINECODEa3b2cce2 或 INLINECODEa3d36a9a)来发起网络请求,可能会觉得有些麻烦(虽然 Node v18 引入了实验性的 fetch,但 Deno 很早就已经完美支持了)。

让我们编写一个简单的网络请求示例:

// 获取 GitHub 用户的 API 信息
const url = "https://api.github.com/users/deno";

// 使用全局可用的 fetch API(与浏览器中完全一致)
const response = await fetch(url);

if (response.ok) {
  const data = await response.json();
  console.log(`用户名称: ${data.login}`);
  console.log(`仓库数量: ${data.public_repos}`);
} else {
  console.error("网络请求失败");
}

运行这段代码需要网络权限:

deno run --allow-net fetch_github.ts

由于使用了 INLINECODEff75075d 对象下的标准 API,这段代码不仅可以在 Deno 中运行,还可以原封不动地移植到浏览器的前端代码中(只要去掉了 INLINECODEe97a344d 命名空间的调用)。这极大地提高了代码的复用性。

总结与最佳实践

经过上面的探讨,我们可以看到 Deno 不仅仅是一个 Node.js 的替代品,它是对 JavaScript 运行时的一次现代化重构。

关键要点回顾:

  • 安全第一:利用 --allow-* 标志,严格控制代码权限,特别是运行不可信脚本时。
  • TypeScript 无缝集成:抛弃复杂的构建配置,直接编写 .ts 文件。
  • 依赖管理现代化:习惯通过 URL 导入模块,并注意锁定版本号,利用缓存机制加速构建。
  • 标准库丰富:Deno 的官方标准库 (std) 质量非常高,涵盖了从文件操作到 Web 服务器的各个方面,在引入第三方库之前,不妨先查看一下标准库。

后续步骤建议:

如果你想进一步探索 Deno,建议尝试编写一个简单的 REST API,或者使用 Deno 的内置测试工具编写单元测试(只需运行 deno test 即可)。拥抱 Deno,意味着拥抱一个更安全、更简洁的 JavaScript 开发未来。希望这篇文章能为你打开 Deno 的大门!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/49103.html
点赞
0.00 平均评分 (0% 分数) - 0