JS三大運(yùn)行時(shí)全面對(duì)比:Node.js vs Bun vs Deno
JavaScript 運(yùn)行時(shí)是指執(zhí)行 JavaScript 代碼的環(huán)境。目前,JavaScript 生態(tài)中有三大運(yùn)行時(shí):Node.js、Bun、Deno。老牌運(yùn)行時(shí) Node.js 的霸主地位正受到 Deno 和 Bun 的挑戰(zhàn),下面就來看看這三個(gè) JS 運(yùn)行時(shí)有什么區(qū)別!
JS 運(yùn)行時(shí)概述
Node.js
Node.js 在 2023 年被 Stack Overflow 開發(fā)者評(píng)為最受歡迎的 Web 技術(shù)。Node.js 于 2009 年推出,允許開發(fā)人員在瀏覽器之外使用 JavaScript,徹底改變了服務(wù)端編程。它擁有強(qiáng)大的生態(tài)系統(tǒng)、龐大的社區(qū),并且經(jīng)過驗(yàn)證且穩(wěn)定。為大型應(yīng)用程序提供 LTS 構(gòu)建?;?V8 JavaScript 引擎構(gòu)建。
多年來,Node.js 一直是服務(wù)端 JavaScript 開發(fā)的支柱,通過第三方工具支持了無數(shù)功能。其提供了巨大的功能和靈活性。豐富的文檔、教程和社區(qū)支持使開發(fā)者可以更輕松地克服挑戰(zhàn)。如果考慮內(nèi)置工具和與 Web API 的兼容性,它是落后于其他兩個(gè)運(yùn)行時(shí)的。
從歷史上看,Node.js 因其安全方法(尤其是在包方面)而受到批評(píng)。然而,社區(qū)和維護(hù)者已經(jīng)顯著改善了這一方面。權(quán)限模型已經(jīng)在 Node.js v20 中實(shí)現(xiàn),這使 Node.js 更加安全。
Deno
Deno 最初由 Node.js 的原始創(chuàng)建者 Ryan Dahl 于 2018 年創(chuàng)建,旨在解決他認(rèn)為 Node.js 中存在的一些問題,比如性能、安全性。它專注于安全性、現(xiàn)代 JavaScript 實(shí)踐和開發(fā)人員體驗(yàn)?;?V8 JavaScript 引擎構(gòu)建并用 Rust 編寫。
與 Node.js 相比,Deno 具有更全面的功能。它對(duì) Web API 和現(xiàn)代標(biāo)準(zhǔn)有很好的支持,并且還支持大多數(shù) NPM 包。Deno 還提供了出色的開發(fā)體驗(yàn),特別是如果使用 TypeScript,它是開箱即用的。Deno 還具有內(nèi)置 linting、代碼格式化程序等優(yōu)勢,節(jié)省一些配置和引導(dǎo)時(shí)間。如果你傾向于開箱即用的設(shè)置,只需啟動(dòng)編輯器,創(chuàng)建一個(gè)main.ts文件,然后就可以開始快樂編碼了!
Bun
Bun 是 2021 年發(fā)布的 JavaScript 運(yùn)行時(shí),它被設(shè)計(jì)為 Node.js 的更快、更精簡、更現(xiàn)代的替代品。它構(gòu)建在 JavaScript Core 和 Zig 之上。旨在成為一個(gè)全功能的運(yùn)行時(shí)環(huán)境和工具包,重點(diǎn)關(guān)注速度、打包、測試和與 Node.js 包的兼容性。最大的優(yōu)勢之一是它的性能。事實(shí)證明,Bun 比 Node.js 和 Deno 都要快。如果 Bun 能夠完成這些目標(biāo),那么它將成為一個(gè)非常有吸引力的選擇。
Bun 的核心賣點(diǎn)是它的性能,其提供了許多基準(zhǔn)測試,顯示出令人驚嘆的速度。使用 Bun 作為包管理器比使用標(biāo)準(zhǔn) NPM 命令要快得多。在現(xiàn)實(shí)應(yīng)用中,尤其是 Web 應(yīng)用,性能差異可能不像基準(zhǔn)測試中那么顯著。
Bun 優(yōu)先考慮簡單性和速度。憑借其內(nèi)置的包管理器,以及與 Node.js 相比改進(jìn)的開發(fā)體驗(yàn),開發(fā)人員可以快速入門,而無需遇到其他運(yùn)行時(shí)可能帶來的初始設(shè)置障礙。
功能對(duì)比
首先來看看這三個(gè)運(yùn)行時(shí)的功能對(duì)比,圖示如下:
- ?:內(nèi)置,指本身提供的功能或特性,無需額外安裝或引入其他庫或框架。
- ??:通過第三方提供的庫、框架或工具支持。
- ?:不可用。
- ??:實(shí)驗(yàn)特性。
運(yùn)行時(shí)特性
特性 | Deno | Bun | Node.js |
升級(jí)工具 | ? | ? | ? |
單個(gè)可執(zhí)行文件安裝 | ? | ? | ? |
LSP | ? | ? | ? |
REPL | ? | ?? | ? |
編譯器 | ? | ? | ? |
持久存儲(chǔ)驅(qū)動(dòng)程序 | ? | ? | ? |
- 升級(jí)工具:更新和管理項(xiàng)目所依賴的軟件包和庫。
- 單個(gè)可執(zhí)行文件安裝:將所有程序文件和依賴項(xiàng)打包成一個(gè)單獨(dú)的可執(zhí)行文件,以便用戶可以簡單地通過運(yùn)行該文件進(jìn)行安裝和部署。
- LSP(Language Server Protocol,語言服務(wù)器協(xié)議):一種用于提供代碼編輯器功能的通信協(xié)議。它使得編輯器可以與語言服務(wù)器進(jìn)行交互,從而獲得代碼補(bǔ)全、跳轉(zhuǎn)到定義、重構(gòu)等功能。
- REPL(Read-Eval-Print Loop,讀取-求值-輸出循環(huán)):一種交互式編程環(huán)境,在其中可以逐行輸入代碼,并立即執(zhí)行并輸出結(jié)果。REPL 通常用于快速測試和驗(yàn)證代碼,無需編譯和構(gòu)建過程。
- 編譯器:是一種將高級(jí)編程語言源代碼轉(zhuǎn)換為低級(jí)機(jī)器代碼或字節(jié)碼的工具。編譯器將代碼進(jìn)行詞法分析、語法分析和轉(zhuǎn)換等處理,最終生成可執(zhí)行文件或中間代碼,以供計(jì)算機(jī)執(zhí)行。
- 持久存儲(chǔ)驅(qū)動(dòng)程序:一種軟件組件或接口,用于與持久化存儲(chǔ)介質(zhì)進(jìn)行交互和管理數(shù)據(jù)的讀取和寫入操作。它提供了對(duì)持久化數(shù)據(jù)的訪問和操作的接口。
測試
特性 | Deno | Bun | Node.js |
基準(zhǔn)測試運(yùn)行器 | ? | ?? | ?? |
測試運(yùn)行器 | ? | ? | ? |
- 基準(zhǔn)測試運(yùn)行器:用于運(yùn)行基準(zhǔn)測試的工具或框架。基準(zhǔn)測試用于評(píng)估代碼的性能和效率,通常通過執(zhí)行一系列測試用例并測量其執(zhí)行時(shí)間來進(jìn)行。
- 測試運(yùn)行器:用于管理和運(yùn)行測試套件的工具或框架。它可以自動(dòng)化執(zhí)行單元測試、集成測試或端到端測試,并提供結(jié)果報(bào)告和日志記錄等功能。
操作系統(tǒng)/平臺(tái)支持
特性 | Deno | Bun | Node.js |
Linux | ? | ? | ? |
Mac OS | ? | ? | ? |
Windows | ? | ?? | ? |
ARM64 | ?? | ?? | ? |
包管理器
特性 | Deno | Bun | Node.js |
package.json 兼容性 | ? | ? | ? |
NPM 取消選擇 | ? | ? | ? |
內(nèi)置包管理器 | ?? | ? | ?? |
URL 引入 | ? | ? | ? |
- package.json 兼容性:指項(xiàng)目中的 package.json 文件與特定工具、平臺(tái)或環(huán)境的兼容性。package.json 是用于描述和管理項(xiàng)目依賴和配置的文件。
- NPM 取消選擇:在使用 NPM 作為包管理器時(shí),選擇不使用某個(gè)特定的功能或設(shè)置。這可能是根據(jù)項(xiàng)目需求或個(gè)人偏好,有意選擇不采用某種功能或行為。
- 內(nèi)置包管理器:集成在特定開發(fā)環(huán)境或平臺(tái)中的默認(rèn)包管理器。這個(gè)包管理器通常提供了一套工具和命令,用于下載、安裝、更新和管理項(xiàng)目的依賴項(xiàng)。
- URL 引入:通過提供遠(yuǎn)程資源的 URL 地址來導(dǎo)入模塊或庫的功能。使用 URL Imports 可以從遠(yuǎn)程位置直接引入代碼或資源,而無需事先下載和安裝。
Web API 兼容性
特性 | Deno | Bun | Node.js |
Fetch | ? | ? | ? |
Web Crypto | ? | ? | ? |
Web Storage | ? | ? | ? |
WebSocket | ? | ?? | ? |
Web Workers | ? | ? | ? |
Import Maps | ? | ? | ? |
- Fetch(Fetch):一種用于發(fā)起網(wǎng)絡(luò)請(qǐng)求的現(xiàn)代 JavaScript API。它提供了一種更簡潔和強(qiáng)大的方式來進(jìn)行數(shù)據(jù)請(qǐng)求和響應(yīng)處理,取代了傳統(tǒng)的 XMLHttpRequest 方法。
- Web Crypto(Web 加密):一組用于在 Web 瀏覽器中執(zhí)行加密操作的 API。它提供了一種安全的方式來處理密碼學(xué)操作,例如生成隨機(jī)數(shù)、進(jìn)行加密和解密等。
- Web Storage(Web 存儲(chǔ)):用于在客戶端瀏覽器中存儲(chǔ)和檢索數(shù)據(jù)的 API。它提供了本地存儲(chǔ)和會(huì)話存儲(chǔ)兩種機(jī)制,分別用于長期保持?jǐn)?shù)據(jù)和臨時(shí)存儲(chǔ)數(shù)據(jù)。
- WebSocket:一種在客戶端和服務(wù)器之間實(shí)現(xiàn)雙向通信的協(xié)議。通過 WebSocket,可以建立持久性的連接,并實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)傳輸和交互。
- Web Workers:一種在瀏覽器中使用多線程進(jìn)行并行計(jì)算的機(jī)制。Web Workers 允許在后臺(tái)運(yùn)行腳本,以避免主線程的阻塞,并提高 Web 應(yīng)用的響應(yīng)性能。
- Import Maps(導(dǎo)入映射):一種在 JavaScript 模塊加載器中配置模塊路徑和別名的功能。導(dǎo)入映射可以簡化模塊導(dǎo)入的過程,并提供更靈活的方式來管理模塊依賴。
安全性
特性 | Deno | Bun | Node.js |
權(quán)限模型 | ? | ? | ? |
可信賴的依賴項(xiàng) | ? | ? | ? |
- 權(quán)限模型:應(yīng)用中用于管理用戶或應(yīng)用對(duì)資源和功能的訪問權(quán)限的系統(tǒng)。權(quán)限模型定義了不同級(jí)別的權(quán)限和許可規(guī)則,并確保只有被授權(quán)的實(shí)體才能執(zhí)行特定操作。
- 可信賴的依賴項(xiàng):開發(fā)中使用的第三方庫或模塊,已經(jīng)得到驗(yàn)證和認(rèn)可,可以放心地被項(xiàng)目所使用。可信賴的依賴項(xiàng)通常具有良好的安全性、穩(wěn)定性和質(zhì)量保證。
開發(fā)工具
特性 | Deno | Bun | Node.js |
代碼格式化工具 | ? | ?? | ?? |
靜態(tài)代碼分析工具 | ? | ?? | ?? |
類型檢查工具 | ? | ?? | ?? |
代碼壓縮工具 | ?? | ? | ?? |
代碼打包工具 | ? | ? | ?? |
依賴項(xiàng)查看器 | ? | ?? | ?? |
- 代碼格式化工具:用于自動(dòng)調(diào)整代碼的格式,例如縮進(jìn)、空格和換行符等。通過使用代碼格式化工具,可以統(tǒng)一代碼樣式,提高代碼的可讀性和一致性。
- 靜態(tài)代碼分析工具:用于檢查源代碼中的潛在問題、錯(cuò)誤或不良實(shí)踐。靜態(tài)代碼分析器會(huì)對(duì)代碼進(jìn)行掃描,并給出相應(yīng)的提示或警告,幫助開發(fā)人員發(fā)現(xiàn)并修復(fù)問題。
- 類型檢查工具:用于靜態(tài)檢查編程語言中的類型錯(cuò)誤。通過類型檢查工具,可以在編譯或運(yùn)行前捕獲到類型相關(guān)的錯(cuò)誤,從而提高代碼質(zhì)量和可靠性。
- 代碼壓縮工具:用于減小源代碼文件的大小。代碼壓縮工具通常會(huì)移除源代碼中的空白字符、注釋和不必要的字符,從而降低文件大小,并提高加載速度。
- 代碼打包工具:用于將多個(gè)模塊或文件打包成一個(gè)或多個(gè)最終部署的文件。通過使用代碼打包工具,可以減少網(wǎng)絡(luò)請(qǐng)求次數(shù),提高前端應(yīng)用的性能和加載速度。
- 依賴項(xiàng)查看器:用于查看項(xiàng)目或應(yīng)用中的各個(gè)依賴項(xiàng)之間的關(guān)系和依賴情況。依賴項(xiàng)查看器可以幫助開發(fā)人員了解項(xiàng)目的依賴結(jié)構(gòu),以便更好地管理和維護(hù)依賴關(guān)系。
語言支持
特性 | Deno | Bun | Node.js |
TypeScript / TSX | ? | ? | ?? |
性能對(duì)比
接下來看看這三個(gè)運(yùn)行時(shí)的網(wǎng)絡(luò)性能比較。重點(diǎn)關(guān)注:靜態(tài)文件傳遞、JSON 響應(yīng)和計(jì)算密集型任務(wù)(素?cái)?shù)計(jì)算)。
- 靜態(tài)文件傳遞:提供靜態(tài)資源服務(wù),將服務(wù)器上指定目錄中的靜態(tài)文件傳遞給客戶端。
- JSON 響應(yīng):接收客戶端請(qǐng)求,生成包含 JSON 數(shù)據(jù)的響應(yīng)并返回給客戶端。
- 計(jì)算密集型任務(wù):接收客戶端傳來的數(shù)值,執(zhí)行大量的 CPU 計(jì)算操作來判斷該數(shù)是否為質(zhì)數(shù),并將結(jié)果返回給客戶端。
為了進(jìn)行準(zhǔn)確的比較,構(gòu)建了一個(gè)自定義的基準(zhǔn)測試工具,并使用 Express.js 作為服務(wù)端平臺(tái)。Express.js 是一個(gè)很好的選擇,因?yàn)榭梢栽谒腥N運(yùn)行時(shí)中使用完全相同的服務(wù)端腳本。源代碼可以在 GitHub 上找到:jsrbench。為了對(duì)服務(wù)端添加負(fù)載,這里使用了 Siege,這是一個(gè)經(jīng)過試驗(yàn)和測試的網(wǎng)絡(luò)服務(wù)器基準(zhǔn)測試實(shí)用工具。
下面是用于基準(zhǔn)測試的服務(wù)端腳本:
import express from "express";
const app = express();
// 使用 BigInt 進(jìn)行修改,并移除 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;
};
// 靜態(tài)資源中間件
app.use("/static", express.static("public"));
// JSON 響應(yīng)
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!`);
}
});
// 將端點(diǎn)收集到數(shù)組中
const endpoints = ["/static/index.html", "/json", "/compute-prime"];
// 通過提供 '0' 自動(dòng)分配端口
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 個(gè)并發(fā)用戶(每秒請(qǐng)求數(shù))
路徑 | Node.js | Deno | Bun |
靜態(tài)文件傳遞 | 1712.37 | 1761.87 | 2559.35 |
JSON 響應(yīng) | 2223.57 | 2772.39 | 4138.38 |
計(jì)算密集型任務(wù) | 2377.44 | 3480.13 | 4321.48 |
100 個(gè)并發(fā)用戶(每秒請(qǐng)求數(shù))
路徑 | Node.js | Deno | Bun |
靜態(tài)文件傳遞 | 2153.87 | 2571.72 | 3468.01 |
JSON 響應(yīng) | 2344.44 | 3468.01 | 4555.89 |
計(jì)算密集型任務(wù) | 2286.53 | 3609.09 | 4341.41 |
根據(jù)給定的條件和具體的基準(zhǔn)測試運(yùn)行結(jié)果:
- Deno 比 Node.js 快大約 33%。
- Bun 比 Node.js 快大約 73%。
Bun 官方也給出了一個(gè)基準(zhǔn)測試的數(shù)據(jù):
- React 服務(wù)端渲染(每秒 HTTP 請(qǐng)求數(shù) (Linux x64)):
- WebSocket 聊天服務(wù)器(每秒發(fā)送的消息數(shù)(Linux x64,32 個(gè)客戶端)):
- 加載一個(gè)巨大的表(每秒平均查詢次數(shù))
可以看到, Bun 是 Deno 的速度兩倍,是 Node.js 速度的四倍。
支持和社區(qū)
這三個(gè)運(yùn)行時(shí)都是開源的,但并非所有項(xiàng)目都完全得到社區(qū)的支持。Node.js 由 OpenJS 基金會(huì)支持,并且嚴(yán)格以社區(qū)和志愿者為基礎(chǔ)。Deno 和 Bun 得到了營利性組織和風(fēng)險(xiǎn)投資支持的項(xiàng)目的支持。
Node.js 有一個(gè)成熟的生態(tài)系統(tǒng)和龐大的社區(qū)。相比之下,Deno 和 Bun 則較為新穎,遇到問題時(shí)可能解決難度更大,但仍然有很多熱情的開發(fā)者愿意分享相關(guān)知識(shí)。此外,Deno 1.28 引入了更好的與 npm 包兼容性,使得從 Node.js 遷移過來的開發(fā)者更容易接受。
下面是 Stack Overflow 上每個(gè)運(yùn)行時(shí)標(biāo)記的問題的數(shù)量(截至 2023 年 9 月):
運(yùn)行時(shí) | 問題數(shù)量 |
Node.js | 466762 |
Deno | 917 |
Bun | 52 |
如你所見,Node.js 相關(guān)的問題最多,這也意味著當(dāng)遇到問題時(shí),更容易得到解決方案。
在 2022 年 State of JavaScript 調(diào)查中,有一個(gè)問題是關(guān)于參與者經(jīng)常使用哪種運(yùn)行時(shí),有將近 30000 名受訪者回答了這個(gè)問題。調(diào)查結(jié)果顯示, Node.js 遙遙領(lǐng)先,Deno 得票數(shù)約為 5300,Bun 得票數(shù)約為 1200。也許我們會(huì)在 2023 年看到 Deno 和 Bun 出現(xiàn)一些新的趨勢。
官方的 Node.js 文檔包括各種指南、大量的 API 參考和入門信息。還提供了有關(guān)其依賴關(guān)系的信息。
Deno 的網(wǎng)站包括一個(gè)非常詳細(xì)的手冊(cè),幫助你熟悉運(yùn)行時(shí)并在項(xiàng)目中開始使用它。第三方模塊頁面很方便,可以了解生態(tài)系統(tǒng)中可用的內(nèi)容。截至 2023 年 8 月,它包含了超過 6000 個(gè)模塊,并提供一些示例代碼。
Bun 的主頁鏈接到了其 Discord、文檔和 GitHub 頁面。自從它發(fā)布以來,文檔已經(jīng)顯著改善?,F(xiàn)在官方文檔中包含了各種主題的信息,例如入門指南、使用打包器和測試運(yùn)行器以及 API 參考,甚至還有指南展示如何使用 Bun 完成常見任務(wù)。
從 Node.js 遷移到 Deno 或 Bun
用純 JavaScript 或 TypeScript 編寫的代碼應(yīng)該可以在任何運(yùn)行時(shí)無縫運(yùn)行。但是,如果使用過 Node.js 的特定功能,那么遷移到其他運(yùn)行時(shí)可能會(huì)比較困難。
從 Node.js 遷移到 Deno
過去,Node.js 模塊的兼容性是 Deno 遷移中的一個(gè)主要問題。不過,現(xiàn)在只需在導(dǎo)入語句前加上node:前綴即可。至于 npm 包,可以在它們前面加上npm:前綴,或者創(chuàng)建一個(gè)deno.js文件,描述 import maps[1] 以供 Deno 解析它們。
如果正在構(gòu)建軟件包/庫,可以查看 Denoify[2]。這是一個(gè)旨在在遷移時(shí)自動(dòng)更改某些文件,并使項(xiàng)目維護(hù)更加容易,適用于 npm 和 deno.land/x[3] 的項(xiàng)目。
從 Node.js 遷移到 Bun
Bun 實(shí)現(xiàn)了大多數(shù) Node-API 函數(shù)。如果項(xiàng)目較小或僅使用常見函數(shù),可能可以直接將其放入 Bun 中并開始使用。對(duì)于大型項(xiàng)目,可能需要重寫代碼來解決挑戰(zhàn)。
Bun 還具有自己的 API。例如,Bun 使用自己的 API 來提供 Web 文件服務(wù)。
Bun.serve({
fetch(req) {
return new Response("Hello!!!");
},
tls: {
key: Bun.file("./key.pem"),
cert: Bun.file("./cert.pem"),
}
});
可以看到,在遷移到 Deno 或 Bun 時(shí),使用它們的原生 API 就意味著代碼與在 Node.j s 中使用的代碼有所不同。這是在轉(zhuǎn)換現(xiàn)有項(xiàng)目時(shí)需要牢記的重要事項(xiàng),同時(shí)在開始新項(xiàng)目時(shí)也要考慮到,因?yàn)槿绻龅皆?Node.js 中不存在的且難以解決的問題,可能會(huì)難以回退到 Node.js。
總結(jié)
Bun 顯然是速度上的贏家,并且在功能上帶來了很多創(chuàng)新。但由于它仍然很新,所以使用它存在風(fēng)險(xiǎn)。
Node.js 的一大優(yōu)勢在于其成熟度和生態(tài)系統(tǒng)的規(guī)模。其仍然是目前最安全的選擇,并久經(jīng)考驗(yàn)。
與 Node.js 相比, Deno 還具有很多優(yōu)勢,其強(qiáng)大的功能使開發(fā)更加順暢,并且可以輕松構(gòu)建高質(zhì)量的復(fù)雜項(xiàng)目。它很安全,雖然比 Node.js 更快,但與 Bun 相比,它還是有點(diǎn)慢的。
總的來說,Node.js 仍然是目前最好的選擇,Deno 具有很多現(xiàn)代化的功能,值得嘗試。如果最關(guān)心速度或只是想了解新技術(shù)的前沿,那么 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。