2023年为何 Deno 还没有干掉 Node.js?
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
阿特伍德定律(Atwood's Law)指出,任何可以用 JavaScript 编写的应用程序最终都将用 Javascript 编写。 这个预言是在 Node.js 诞生前两年提出的,结果证明也是非常正确的。 在浏览器上下文之外运行 JavaScript 环境的到来,鼓励开发者开始用纯 JS 编写服务器、CLI,甚至机器学习算法模型。
Any application that can be written in JS will eventually be written in JavaScript!
尽管 Node.js 是在服务器上运行 JavaScript 的事实标准,但并不是每个人都对它非常满意,尤其是它的作者!所以才有了其他的 JS Runtime,比如Deno等替代方案。以下是已发布文章的传送门:
《 JS Runtime vs. JS Engine!Deno/Node是运行时!》《 前有Deno、后有Bun、Node.js已穷途末路?》《 Node.js、Deno、Bun 6大典型场景性能大PK?》《 Node.js V20.0如期发布!盘点9大新特性! 》《 盘点全网最火的6 JavaScript 运行时!Node/Deno/Bun 在列! 》
毫无疑问,Node.js 目前是世界上最流行和使用最广泛的 JavaScript 运行时。 但是,Deno 在 2018 年的到来引起了巨大的轰动,开发人员可以使用具有现代功能的更安全的框架。
Deno 与 Node.js 由同一作者 Ryan Dahl 开发, Deno 被称为 Node.js 的继任者并修复了 Node.js 中一些主要设计缺陷。 尽管如此,Deno 的发布引发了很多关于 Deno 是否正在取代 Node.js 的猜测。 但是,到目前为止,好像一切都比较平和, Node.js 仍然是大多数开发人员的不二选择。
本文将尝试解释为什么开发者对 Deno 的采纳变得非常缓慢,以及为什么人们仍然更喜欢 Node.js,同时对 Node.js 和 Deno 做一个比较。
Node.js 是一个流行的服务器端、开源、跨平台的 JavaScript 运行时,它建立在谷歌的 V8 JS 引擎之上。 自 2009 年由 Ryan Dahl 发布 以来,Node.js 一直主导着 Web 开发世界。目前 Node.js 的最新版本是 V20.0.0。
Node.js 专注于事件驱动的 HTTP 服务器。 在处理请求时,它运行一个向系统注册的单线程事件循环,每个传入请求都会触发一个 JavaScript 回调函数。 回调函数能够使用非阻塞 I/O 的形式处理请求。
此外,Node.js 还可以生成线程来执行阻塞或 CPU 密集型任务,从而在 CPU 内核之间分配负载。 与大多数通过线程扩展的竞争框架不同,Node.js 基于回调函数的扩展机制可以以最少的内存使用来处理更多请求。
由于其异步 I/O 和事件驱动架构,Node.js 是轻量级的,非常适合可扩展、数据密集型、实时的 Web 应用程序,这些应用程序可以在分布式设备上运行。
比如在下面的 hello world 示例中,Node.js 可以同时处理多个连接。 每次连接时,都会触发回调,但如果没有工作要做,Node.js 就会休眠。
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
// 输出hello world
res.end('Hello World');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
根据两个运行时的创建者 Ryan Dahl 的说法,Node.js 具有三个明显的缺点:
设计不当的模块系统依赖于集中分布(Centralized Distribution)不稳定的遗留 API缺乏安全性
而 Deno 解决了这三个问题并提供了更好的体验。
Deno(/diːnoʊ/,发音为 dee-no)是一个安全的 JavaScript、TypeScript 和 WebAssembly 运行时,同时具有出色的开发人员体验。 它建立在谷歌 JavaScript 运行时 V8、Rust 和 Tokio 之上。Deno 构建于以下几个核心能力:
语言:Deno 的核心是用 Rust 编写,而 Node 的核心是用 C 编写Tokio事件循环:是 Rust 编程语言的异步运行时, 它提供了编写网络应用程序所需的构建块,同时提供了针对各种系统的灵活性,从具有数十个内核的大型服务器到小型嵌入式设备。TypeScript 支持:Deno 开箱即用地支持 JavaScript 和 TypeScriptV8 引擎:Google 的 JavaScript 运行时,同时也用于 Chrome 和 Node 等
下表从语言支持(Deno 支持 TypeScript)、包管理(Node.js 使用 NPM)、安全&权限(Deno 支持沙箱)、代码集成(都是 V8 引擎)、机器执行(Deno 基于 Tokio 等 Rust 语言编写的底层能力)等诸多维度展示了 Node.js 和 Deno 的主要区别:
总之,Deno 构建于 Rust 之上,而 Node.js 构建于 C 之上。Deno 具有沙盒功能,更加安全,支持开箱即用的 TypeScript 和可靠的标准包集合,同时 Deno 也具有安全默认值,除非明确启用,否则无法访问文件、网络或其他环境。Deno 采用 Web 平台标准,始终与单个可执行文件一起分发,并具有内置的开发工具以提供高效、安全的运行时。
测试数据表明,在 HTTP 吞吐量、请求延迟等方面 Node.js 和 Deno 也是各有所长。
在下面的示例中,add 和 multiply 是 Deno 通过从远程模块导入并使用的示例:
/**
* remote.ts
*/
import {
add,
multiply,
} from "https://x.nest.land/[email protected]/source/index.js";
function totalCost(outbound: number, inbound: number, tax: number): number {
return multiply(add(outbound, inbound), tax);
}
console.log(totalCost(19, 31, 1.2));
console.log(totalCost(45, 27, 1.15));
/**
* Output
* 60
* 82.8
*/
接下来一起看看Deno、Node.js在几个关键维度的差异。
如上面的代码示例,借助 Deno开发人员可以直接从 URL 安装包,而无需像 npm 这样的集中式注册表。 尽管直接从 URL 下载包存在风险,例如:如果托管包的服务器遭到破坏,攻击者可能会修改代码包含恶意功能,但是 Deno 在一定程度上可以缓解这种问题。
总之,在 Deno中,没有包管理器的概念,外部模块直接导入到本地模块中。
这又带来了一个问题,即如何在没有包管理器的情况下管理远程依赖。在具有许多依赖性的大型项目中,如果将模块全部单独导入到单个模块中,则更新模块将变得很麻烦且耗时。
在 Deno 中解决此问题的标准做法是创建一个 deps.ts 文件。此文件中引用了所有必需的远程依赖关系,并且重新导出了所需的方法和类。本地模块从 deps.ts 导入所需方法和类,而不是远程依赖。
这样就可以轻松跨大型代码库更新模块,并解决“程序包管理器问题”(如果它存在的话)。开发依赖项也可以在单独的 dev_deps.ts 文件中进行管理。
/**
* deps.ts 从远程的 Ramda 模块中重新导出所需方法。
**/
export {
add,
multiply,
} from "https://x.nest.land/[email protected]/source/index.js";
而 Node.js 中借助于 npm 等包管理器很好的解决了此类问题。
Node.js 诞生于 JavaScript 中引入 Promises 或 async/await 之前。 因此,大多数 API 都被设计为接受错误优先的回调,这种方法经常会产生冗长而复杂的代码,比如下面的多层回调,又称为回调地狱。
login('john_d', (user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
getUser(comments[0].userId, (user) => {
console.log(user);
});
});
});
});
但是现在,Node.js 开发人员虽然可以使用 async/await 语法,还必须保证向后兼容, 因为API 经常更改且不稳定。
而 Deno 完全不同,不需要封装在 async 函数中,因为它已经使用 await 并支持最新的 JavaScript 功能。 因此,Deno 通过为开发人员简化此过程,促进将基于未来的 API 绑定到 JavaScript Promise 中。
Deno 还旨在与浏览器中运行的 JavaScript 代码使用的 Web 平台 API 兼容。 以符合标准的方式支持许多最流行的高级 Web API,例如 Fetch、Web Storage、Web Workers 和 Broadcast Channel 等等。
安全是 Ryan Dahl 创建 Deno 的主要原因之一。Deno 在禁止访问文件系统的安全沙箱环境中执行所有代码,除非明确请求授权。下面是未授权场景下的安全访问示例图:
Deno 的几个命令行标志可以在运行时使用,以启用脚本的特定功能。 默认情况下,这些功能处于非活动状态,除非明确开启:
— allow-env:允许访问环境变量— allow-hrtime:允许高分辨率时间测量— allow-net:允许网络访问— allow-read :允许读取文件系统— allow-write:允许文件系统写访问— allow-run:允许运行子进程
相比之下,Node.js 并非一个高度安全的框架。 例如:Node 不需要明确授予读取或写入文件系统的访问权限,但是它不是沙盒。
node.js v20引入了权限模型,但目前是一种实验性机制,用于在执行期间限制对特定资源的访问。主要包括: --allow-fs-read、--allow-fs-write 、--allow-child-process 、--allow-worker 、--experimental-permission 等等
因此,任何第三方库都可能引起诸多麻烦。 当然,有一些标准的安全措施可以防止对 Node.js 的跨站点请求伪造 (CSRF) 和跨站点脚本 (XSS) 等威胁。 这些机制涉及检查日志、正确管理错误和异常以及验证用户输入。
因此,尽管习惯了安全模块的严格性,但 Deno 总体来看是更安全环境的最佳选择。比如下面的代码,在Deno中运行默认保证安全。
import { Application } from "https://deno.land/x/abc/mod.ts";
const app = new Application();
app
.get("/", c => {
return `Hello, Abc! args: ${JSON.stringify(Deno.args)}`;
})
.start({ port: 8080 });
TypeScript 是 JavaScript 的超集,允许用户添加可选的静态类型。 Deno 通过使用带有缓存技术的 TypeScript 编译器来开箱即用的支持 TypeScript。 因此,Deno 可以编译、类型检查和执行代码,而无需将其转换为 JavaScript。
尽管 Node.js 缺乏像 Deno 那样固有的 TypeScript 支持,但由于 TypeScript 包的存在,TypeScript 已成为 Node.js 社区中一种众所周知且广泛使用的语言。 因此,TypeScript 和 Node 目前两者都能很好的集成工作。
如果开发者已经安装了 Node 和 npm,则只需运行如下命令:
npm install -g typescript
即可在机器上全局安装 TypeScript。 接下来,运行 tsc --init 来创建 TypeScript 配置文件。 然后可以使用 TypeScript 编译器 tsc 来编译 .ts 文件。下面是 tsconfig.json 的一个配置示例:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"files": [
"core.ts",
//...更多文件配置
]
}
总之,如果开发者喜欢 TypeScript 并且更愿意尝试一种新颖的框架,那么 Deno 是合适的框架。 但是,在 Deno 中使用 TypeScript 进行开发不需要任何额外的配置。
自 ry 宣布在 Deno 两周年的 2020 年 5 月 13 日发布首个 Deno 1.0 正式版后,Deno 一直受到开发者们的持续关注。但是,这么多年过去了,为什么 Deno 还没有取代 Node?
当 Node.js 最初发布时,开发人员开始在实际项目中使用它。 多年来,为了满足开发社区的需求, Node.js 发生了很大的变化。 Node.js 现在是使用最广泛的 JavaScript 运行时,而 Deno、Bun 等相比之下只算是蹒跚学步阶段。
由于 Node.js 的广泛使用,社区、专业技术开发人员数量也迅速激增。 同时,Node.js 包系统基于 npm 构建,功能非常广泛且易于使用。 因此,当开发者主要关注灵活性和熟悉度时,Node.js 仍然是最佳选择。
然而,Node.js 并不完美, Deno 在 Node 落后的各个重要领域取得了重大进步,尤其是在安全性和标准合规性方面。 而且 Deno 的单一二进制模式使其比 Node.js 更具可移植性,而且其开放式模块架构可能会成为未来依赖导入的规范。
尽管 Deno 的社区很活跃,但相比 Node.js 仍然很小,特定解决方案的 Deno 包并不完善,而且 Node 兼容层也并非完美。
另一方面,Node.js 是高度模块化的, 其充分利用了包管理器 npm,可以使得开发者快速找到三方流行的库。借助于第三方库,可以在 Node.js 中实现 Deno 提供的许多功能。 因此,如果开发者只希望拥有 Deno 的部分功能,那么大多数情况下依然可以选择使用 Node.js ,尽管不如 Deno 般丝滑。
在 Node.js 中权限的易用性是另一个重要特性。 开发者可以在平台上快速收到反馈,无需经过各种授权。 Node.js 允许开发者使用最少的工作来快速编码,因为它很少依赖特定的权限。 这对于需要定期更新、大型专家团队的动态项目非常合适。
相比之下,Deno 仍处于学步段,其尚未在生产系统中经过全面测试,开发人员仍在适应这种新的运行时环境。 更多的开发者只是对 Deno 这种运行时保持好奇,或者持续关注。当然,Deno 具有巨大的前景,因为它解决了 Node.js 的重要缺点,包括:安全性、模块、回调和中央分发系统。
开发者有理由相信,随着 Deno 社区的逐步完善、壮大,Deno 逐渐会成为新的 Web 开发首选运行时。不过现阶段来看,大多数开发人员对 Node.js 的发展依然保持乐观,从而不会忙着迁移到其他运行时,比如 Deno、又比如 Bun。
本文主要大家介绍为什么2023年了,大多数开发者依然用 Node.js 而非 Deno。文章从什么是 Node.js 、什么是 Deno 、详细对比 Deno 与 Node 的区别 、为什么开发人员选择 Node 而不是 Deno等多个维度展开。
因为篇幅有限,文章并没有过多展开,如果有兴趣,文末的参考资料提供了优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!
https://github.com/tokio-rs/tokio-io
https://www.freecodecamp.org/news/what-is-node-js/
https://medium.com/deno-the-complete-reference/sandboxing-in-deno-b3d514d88b63
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
https://tokio.rs/tokio/tutorial
https://cult.honeypot.io/reads/deno-vs-node-main-differences/
https://externlabs.com/blogs/deno-vs-nodejs/
原文链接:https://cult.honeypot.io/reads/deno-vs-node-main-differences/
原文作者:Honeypot
翻译作者:头条《高级前端进阶》
- 0000
- 00020
- 0000
- 0002
- 0000