全文约 5100 字,预计浏览须要 15 分钟。
JavaScript 运行时是指 执行 JavaScript 代码的环境。目前,JavaScript 生态中有三大运行时:Node.js、Bun、Deno。老牌运行时 Node.js 的霸主位置正受到 Deno 和 Bun 的挑战,上面就来看看这三个 JS 运行时有什么区别!
JS 运行时概述
Node.js
Node.js 在 2023 年被 Stack Overflow 开发者评为最受欢迎的 Web 技术。Node.js 于 2009 年推出,容许开发人员在浏览器之外应用 JavaScript,彻底改变了服务端编程。它领有弱小的生态系统、宏大的社区,并且通过验证且稳固。为大型应用程序提供 LTS 构建。基于 V8 JavaScript 引擎构建。
多年来,Node.js 始终是服务端 JavaScript 开发的支柱,通过第三方工具反对了有数性能。其提供了微小的性能和灵活性。丰盛的文档、教程和社区反对使开发者能够更轻松地克服挑战。如果思考内置工具和与 Web API 的兼容性,它是落后于其余两个运行时的。
从历史上看,Node.js 因其平安办法(尤其是在包方面)而受到批评。然而,社区和维护者曾经显着改善了这一方面。权限模型曾经在 Node.js v20 中实现,这使 Node.js 更加平安。
Deno
Deno 最后由 Node.js 的原始创建者 Ryan Dahl 于 2018 年创立,旨在解决他认为 Node.js 中存在的一些问题,比方性能、安全性。它专一于安全性、古代 JavaScript 实际和开发人员体验。基于 V8 JavaScript 引擎构建并用 Rust 编写。
与 Node.js 相比,Deno 具备更全面的性能。它对 Web API 和古代规范有很好的反对,并且还反对大多数 NPM 包。Deno 还提供了杰出的开发体验,特地是如果应用 TypeScript,它是开箱即用的。Deno 还具备内置 linting、代码格式化程序等劣势,节俭一些配置和疏导工夫。如果你偏向于开箱即用的设置,只需启动编辑器,创立一个 main.ts
文件,而后就能够开始高兴编码了!
Bun
Bun 是 2021 年公布的 JavaScript 运行时,它被设计为 Node.js 的更快、更精简、更古代的替代品。它构建在 JavaScript Core 和 Zig 之上。旨在成为一个全功能的运行时环境和工具包,重点关注速度、打包、测试和与 Node.js 包的兼容性。最大的劣势之一是它的性能。事实证明,Bun 比 Node.js 和 Deno 都要快。如果 Bun 可能实现这些指标,那么它将成为一个十分有吸引力的抉择。
Bun 的外围卖点是它的 性能,其提供了许多基准测试,显示出令人惊叹的速度。应用 Bun 作为包管理器比应用规范 NPM 命令要快得多。在事实利用中,尤其是 Web 利用,性能差别可能不像基准测试中那么显着。
Bun 优先思考简略性和速度。凭借其内置的包管理器,以及与 Node.js 相比改良的开发体验,开发人员能够疾速入门,而无需遇到其余运行时可能带来的初始设置障碍。
性能比照
首先来看看这三个运行时的性能比照,图示如下:
- ✅:内置,指自身提供的性能或个性,无需额定装置或引入其余库或框架。
- 📦:通过第三方提供的库、框架或工具反对。
- ❌:不可用。
- 💡:试验个性。
运行时个性
个性 | Deno | Bun | Node.js |
---|---|---|---|
降级工具 | ✅ | ✅ | ❌ |
单个可执行文件装置 | ✅ | ✅ | ❌ |
LSP | ✅ | ❌ | ❌ |
REPL | ✅ | 📦 | ✅ |
编译器 | ✅ | ✅ | ❌ |
长久存储驱动程序 | ✅ | ✅ | ❌ |
- 降级工具:更新和治理我的项目所依赖的软件包和库。
- 单个可执行文件装置:将所有程序文件和依赖项打包成一个独自的可执行文件,以便用户能够简略地通过运行该文件进行装置和部署。
- LSP(Language Server Protocol,语言服务器协定):一种用于提供代码编辑器性能的通信协议。它使得编辑器能够与语言服务器进行交互,从而取得代码补全、跳转到定义、重构等性能。
- REPL(Read-Eval-Print Loop,读取 - 求值 - 输入循环):一种交互式编程环境,在其中能够逐行输出代码,并立刻执行并输入后果。REPL 通常用于疾速测试和验证代码,无需编译和构建过程。
- 编译器:是一种将高级编程语言源代码转换为低级机器代码或字节码的工具。编译器将代码进行词法剖析、语法分析和转换等解决,最终生成可执行文件或中间代码,以供计算机执行。
- 长久存储驱动程序:一种软件组件或接口,用于与长久化存储介质进行交互和治理数据的读取和写入操作。它提供了对长久化数据的拜访和操作的接口。
测试
个性 | Deno | Bun | Node.js |
---|---|---|---|
基准测试运行器 | ✅ | 💡 | 💡 |
测试运行器 | ✅ | ✅ | ✅ |
- 基准测试运行器:用于运行基准测试的工具或框架。基准测试用于评估代码的性能和效率,通常通过执行一系列测试用例并测量其执行工夫来进行。
- 测试运行器:用于治理和运行测试套件的工具或框架。它能够自动化执行单元测试、集成测试或端到端测试,并提供后果报告和日志记录等性能。
操作系统 / 平台反对
个性 | Deno | Bun | Node.js |
---|---|---|---|
Linux | ✅ | ✅ | ✅ |
Mac OS | ✅ | ✅ | ✅ |
Windows | ✅ | 💡 | ✅ |
ARM64 | 💡 | 💡 | ✅ |
包管理器
个性 | Deno | Bun | Node.js |
---|---|---|---|
package.json 兼容性 | ✅ | ✅ | ✅ |
NPM 勾销抉择 | ✅ | ❌ | ❌ |
内置包管理器 | 📦 | ✅ | 📦 |
URL 引入 | ✅ | ❌ | ❌ |
- package.json 兼容性:指我的项目中的 package.json 文件与特定工具、平台或环境的兼容性。package.json 是用于形容和治理我的项目依赖和配置的文件。
- NPM 勾销抉择:在应用 NPM 作为包管理器时,抉择不应用某个特定的性能或设置。这可能是依据我的项目需要或集体偏好,无意抉择不采纳某种性能或行为。
- 内置包管理器:集成在特定开发环境或平台中的默认包管理器。这个包管理器通常提供了一套工具和命令,用于下载、装置、更新和治理我的项目的依赖项。
- URL 引入:通过提供近程资源的 URL 地址来导入模块或库的性能。应用 URL Imports 能够从近程地位间接引入代码或资源,而无需当时下载和装置。
Web API 兼容性
个性 | Deno | Bun | Node.js |
---|---|---|---|
Fetch | ✅ | ✅ | ✅ |
Web Crypto | ✅ | ✅ | ✅ |
Web Storage | ✅ | ❌ | ❌ |
WebSocket | ✅ | 📦 | ❌ |
Web Workers | ✅ | ✅ | ❌ |
Import Maps | ✅ | ❌ | ❌ |
- Fetch(Fetch):一种用于发动网络申请的古代 JavaScript API。它提供了一种更简洁和弱小的形式来进行数据申请和响应解决,取代了传统的 XMLHttpRequest 办法。
- Web Crypto(Web 加密):一组用于在 Web 浏览器中执行加密操作的 API。它提供了一种平安的形式来解决密码学操作,例如生成随机数、进行加密和解密等。
- Web Storage(Web 存储):用于在客户端浏览器中存储和检索数据的 API。它提供了本地存储和会话存储两种机制,别离用于长期保持数据和长期存储数据。
- WebSocket:一种在客户端和服务器之间实现双向通信的协定。通过 WebSocket,能够建设持久性的连贯,并实现实时数据传输和交互。
- Web Workers:一种在浏览器中应用多线程进行并行计算的机制。Web Workers 容许在后盾运行脚本,以防止主线程的阻塞,并进步 Web 利用的响应性能。
- Import Maps(导入映射):一种在 JavaScript 模块加载器中配置模块门路和别名的性能。导入映射能够简化模块导入的过程,并提供更灵便的形式来治理模块依赖。
安全性
个性 | Deno | Bun | Node.js |
---|---|---|---|
权限模型 | ✅ | ❌ | ✅ |
可信赖的依赖项 | ❌ | ✅ | ❌ |
- 权限模型:利用中用于治理用户或利用对资源和性能的拜访权限的零碎。权限模型定义了不同级别的权限和许可规定,并确保只有被受权的实体能力执行特定操作。
- 可信赖的依赖项:开发中应用的第三方库或模块,曾经失去验证和认可,能够释怀地被我的项目所应用。可信赖的依赖项通常具备良好的安全性、稳定性和质量保证。
开发工具
个性 | Deno | Bun | Node.js |
---|---|---|---|
代码格式化工具 | ✅ | 📦 | 📦 |
动态代码剖析工具 | ✅ | 📦 | 📦 |
类型查看工具 | ✅ | 📦 | 📦 |
代码压缩工具 | 📦 | ✅ | 📦 |
代码打包工具 | ✅ | ✅ | 📦 |
依赖项查看器 | ✅ | 📦 | 📦 |
- 代码格式化工具:用于主动调整代码的格局,例如缩进、空格和换行符等。通过应用代码格式化工具,能够对立代码款式,进步代码的可读性和一致性。
- 动态代码剖析工具:用于查看源代码中的潜在问题、谬误或不良实际。动态代码分析器会对代码进行扫描,并给出相应的提醒或正告,帮忙开发人员发现并修复问题。
- 类型查看工具:用于动态查看编程语言中的类型谬误。通过类型查看工具,能够在编译或运行前捕捉到类型相干的谬误,从而进步代码品质和可靠性。
- 代码压缩工具:用于减小源代码文件的大小。代码压缩工具通常会移除源代码中的空白字符、正文和不必要的字符,从而升高文件大小,并进步加载速度。
- 代码打包工具:用于将多个模块或文件打包成一个或多个最终部署的文件。通过应用代码打包工具,能够缩小网络申请次数,进步前端利用的性能和加载速度。
- 依赖项查看器:用于查看我的项目或利用中的各个依赖项之间的关系和依赖状况。依赖项查看器能够帮忙开发人员理解我的项目的依赖构造,以便更好地治理和保护依赖关系。
语言反对
个性 | Deno | Bun | Node.js |
---|---|---|---|
TypeScript / TSX | ✅ | ✅ | 📦 |
性能比照
接下来看看这三个运行时的网络性能比拟。重点关注:动态文件传递、JSON 响应和计算密集型工作(素数计算)。
- 动态文件传递:提供动态资源服务,将服务器上指定目录中的动态文件传递给客户端。
- JSON 响应:接管客户端申请,生成蕴含 JSON 数据的响应并返回给客户端。
- 计算密集型工作:接管客户端传来的数值,执行大量的 CPU 计算操作来判断该数是否为质数,并将后果返回给客户端。
为了进行精确的比拟,构建了一个自定义的基准测试工具,并应用 Express.js 作为服务端平台。Express.js 是一个很好的抉择,因为能够在所有三种运行时中应用完全相同的服务端脚本。源代码能够在 GitHub 上找到:jsrbench。为了对服务端增加负载,这里应用了 Siege,这是一个通过试验和测试的网络服务器基准测试实用工具。
上面是用于基准测试的服务端脚本:
import express from "express";
const app = express();
// 应用 BigInt 进行批改,并移除 NaN/Infinity 查看
const checkPrime = function (n) {if (n % 1n || n < 2n) return 0;
if (n == leastFactor(n)) return 1;
return 0;
};
const leastFactor = function (n) {if (n == 0n) return 0;
if (n % 1n || n * n < 2n) return 1;
if (n % 2n == 0) return 2;
if (n % 3n == 0) return 3;
if (n % 5n == 0) return 5;
for (let i = 7n; i * i <= n; i += 30n) {if (n % i == 0n) return i;
if (n % (i + 4n) == 0) return i + 4n;
if (n % (i + 6n) == 0) return i + 6n;
if (n % (i + 10n) == 0) return i + 10n;
if (n % (i + 12n) == 0) return i + 12n;
if (n % (i + 16n) == 0) return i + 16n;
if (n % (i + 22n) == 0) return i + 22n;
if (n % (i + 24n) == 0) return i + 24n;
}
return n;
};
// 动态资源中间件
app.use("/static", express.static("public"));
// JSON 响应
app.get("/json", (req, res) => {
res.json({
message: "Hello, World!",
number: 5,
literal: `(${4}+${4})*${21.2}/${2}=${84.8}`,
});
});
// 模仿 CPU 密集型操作
app.get("/compute-prime", (_req, res) => {
const toCheck = 263n;
if (checkPrime(263n)) {res.send(`Prime number ${toCheck} is a prime!`);
} else {res.send(`Prime number ${toCheck} is not a prime!`);
}
});
// 将端点收集到数组中
const endpoints = ["/static/index.html", "/json", "/compute-prime"];
// 通过提供 '0' 主动调配端口
const server = app.listen(0, () => {
const fullEndpoints = endpoints.map((endpoint) => `http://127.0.0.1:${server.address().port}${endpoint}`,
);
console.log(JSON.stringify({BENCHMARKABLE_ENDPOINTS: fullEndpoints,}));
});
10 个并发用户(每秒申请数)
门路 | Node.js | Deno | Bun |
---|---|---|---|
动态文件传递 | 1712.37 | 1761.87 | 2559.35 |
JSON 响应 | 2223.57 | 2772.39 | 4138.38 |
计算密集型工作 | 2377.44 | 3480.13 | 4321.48 |
100 个并发用户(每秒申请数)
门路 | Node.js | Deno | Bun |
---|---|---|---|
动态文件传递 | 2153.87 | 2571.72 | 3468.01 |
JSON 响应 | 2344.44 | 3468.01 | 4555.89 |
计算密集型工作 | 2286.53 | 3609.09 | 4341.41 |
依据给定的条件和具体的基准测试运行后果:
- Deno 比 Node.js 快大概 33%。
- Bun 比 Node.js 快大概 73%。
Bun 官网也给出了一个基准测试的数据:
- React 服务端渲染(每秒 HTTP 申请数(Linux x64)):
- WebSocket 聊天服务器(每秒发送的音讯数(Linux x64,32 个客户端)):
- 加载一个微小的表(每秒均匀查问次数)
能够看到,Bun 是 Deno 的速度两倍,是 Node.js 速度的四倍。
反对和社区
这三个运行时都是开源的,但并非所有我的项目都齐全失去社区的反对。Node.js 由 OpenJS 基金会反对,并且严格以社区和志愿者为根底。Deno 和 Bun 失去了营利性组织和风险投资反对的我的项目的反对。
Node.js 有一个成熟的生态系统和宏大的社区。相比之下,Deno 和 Bun 则较为新鲜,遇到问题时可能解决难度更大,但依然有很多激情的开发者违心分享相干常识。此外,Deno 1.28 引入了更好的与 npm 包兼容性,使得从 Node.js 迁徙过去的开发者更容易接受。
上面是 Stack Overflow 上每个运行时标记的问题的数量(截至 2023 年 9 月):
运行时 | 问题数量 |
---|---|
Node.js | 466762 |
Deno | 917 |
Bun | 52 |
如你所见,Node.js 相干的问题最多,这也意味着当遇到问题时,更容易失去解决方案。
在 2022 年 State of JavaScript 考察中,有一个问题是对于参与者常常应用哪种运行时,有将近 30000 名受访者答复了这个问题。考察结果显示,Node.js 遥遥领先,Deno 得票数约为 5300,Bun 得票数约为 1200。兴许咱们会在 2023 年看到 Deno 和 Bun 呈现一些新的趋势。
官网的 Node.js 文档包含各种指南、大量的 API 参考和入门信息。还提供了无关其依赖关系的信息。
Deno 的网站包含一个十分具体的手册,帮忙你相熟运行时并在我的项目中开始应用它。第三方模块页面很不便,能够理解生态系统中可用的内容。截至 2023 年 8 月,它蕴含了超过 6000 个模块,并提供一些示例代码。
Bun 的主页链接到了其 Discord、文档和 GitHub 页面。自从它公布以来,文档曾经显著改善。当初官网文档中蕴含了各种主题的信息,例如入门指南、应用打包器和测试运行器以及 API 参考,甚至还有指南展现如何应用 Bun 实现常见工作。
从 Node.js 迁徙到 Deno 或 Bun
用纯 JavaScript 或 TypeScript 编写的代码应该能够在任何运行时无缝运行。然而,如果应用过 Node.js 的特定性能,那么迁徙到其余运行时可能会比拟艰难。
从 Node.js 迁徙到 Deno
过来,Node.js 模块的兼容性是 Deno 迁徙中的一个次要问题。不过,当初只需在导入语句前加上 node:
前缀即可。至于 npm 包,能够在它们后面加上 npm:
前缀,或者创立一个 deno.js
文件,形容 import maps[1] 以供 Deno 解析它们。
如果正在构建软件包 / 库,能够查看 Denoify[2]。这是一个旨在在迁徙时主动更改某些文件,并使我的项目保护更加容易,实用于 npm 和 deno.land/x[3] 的我的项目。
从 Node.js 迁徙到 Bun
Bun 实现了大多数 Node-API 函数。如果我的项目较小或仅应用常见函数,可能能够间接将其放入 Bun 中并开始应用。对于大型项目,可能须要重写代码来解决挑战。
Bun 还具备本人的 API。例如,Bun 应用本人的 API 来提供 Web 文件服务。
Bun.serve({fetch(req) {return new Response("Hello!!!");
},
tls: {key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
}
});
能够看到,在迁徙到 Deno 或 Bun 时,应用它们的原生 API 就意味着代码与在 Node.j s 中应用的代码有所不同。这是在转换现有我的项目时须要牢记的重要事项,同时在开始新我的项目时也要思考到,因为如果遇到在 Node.js 中不存在的且难以解决的问题,可能会难以回退到 Node.js。
总结
Bun 显然是速度上的赢家,并且在性能上带来了很多翻新。但因为它依然很新,所以应用它存在危险。
Node.js 的一大劣势在于其成熟度和生态系统的规模。其依然是目前最平安的抉择,并久经考验。
与 Node.js 相比,Deno 还具备很多劣势,其弱小的性能使开发更加顺畅,并且能够轻松构建高质量的简单我的项目。它很平安,尽管比 Node.js 更快,但与 Bun 相比,它还是有点慢的。
总的来说,Node.js 依然是目前最好的抉择,Deno 具备很多现代化的性能,值得尝试。如果最关怀速度或只是想理解新技术的前沿,那么 Bun 就是你的首选工具。
[1]import maps:https://deno.land/manual@v1.36.1/basics/import\_maps
[2]Denoify:https://github.com/garronej/denoify
[3]deno.land/x:https://deno.land/x
往期举荐
VS Code 中应用 Git 实际,学会了效率翻倍!
微软最热门的 10 款前端开源我的项目!
JavaScript 终于原生反对数组分组了!
Next.js 13.5 正式公布,速度大幅晋升!
多图预警,前端应该把握的浏览器调试技巧大揭秘!
居然能够在一个我的项目中混用 Vue 和 React?
图解 60 个 CSS 选择器,一网打尽!