Ryan's 有关 Node.js 的遗憾在 2018 年 JSConf EU 上著名的演讲 “我对 Node.js 遗憾的十件事” 有充分的记录。总而言之,他感叹缺乏对安全性的关注、通过 node_modules 解析模块、浏览器工作方式的各种偏差以及其他问题,他开始在 Deno 中修复这些错误。
创新互联建站是一家专注网站建设、网络营销策划、小程序制作、电子商务建设、网络推广、移动互联开发、研究、服务为一体的技术型公司。公司成立十年以来,已经为1000多家茶艺设计各业的企业公司提供互联网服务。现在,服务的1000多家客户与我们一路同行,见证我们的成长;未来,我们一起分享成功的喜悦。
在本文中,我们将讨论 Deno 的创建原因以及它与 Node.js 相比的优缺点。还将对 Deno 的怪癖(quirks)和功能做一个实用概述,以便您决定它是否适合于您的下一个项目。
Deno 做为一个独立的、自包含的二进制文件没有任何依赖。你可以通过多种方式安装 Deno,具体取决于你的操作系统。最简单的方法是下载并执行一个 shell 脚本,如下所示:
# Linux and macOS
$ curl -fsSL https://deno.land/x/install/install.sh | sh
# Windows PowerShell
$ iwr https://deno.land/x/install/install.ps1 -useb | iex
一旦你为你的操作系统执行了适当的命令,Deno CLI 二进制文件将会下载到你的计算机。根据你选择的安装方法,你需要添加这个二进制文件位置到你的 PATH。
你可以在 Bash 中添加以下行到你的 $HOME/bash_profile 文件来执行此操作。你可能需要开启一个新的 shell 会话来使更改生效。
export DENO_INSTALL="$HOME/.deno"
export PATH="$DENO_INSTALL/bin:$PATH"
运行以下命令,验证 Deno 的安装版本。如果这个 CLI 已经下载成功并且添加到您的 PATH,控制台应该会打印输出 Deno 版本信息。
$ deno --version
deno 1.14.2 (release, x86_64-unknown-linux-gnu)
v8 9.4.146.16
typescript 4.4.2
如果你的 Deno 版本已过时,可以通过 upgrade 子命令升级到最新的 release 版本。
$ deno upgrade
Looking up latest version
Found latest version 1.14.2
Checking https://github.com/denoland/deno/releases/download/v1.14.2/deno-x86_64-unknown-linux-gnu.zip
31.5 MiB / 31.5 MiB (100.0%)
Deno is upgrading to version 1.14.2
Archive: /tmp/.tmpfdtMXE/deno.zip
inflating: deno
Upgraded successfully
接下来,编写一个常用的 Hello world 程序来验证是否可以正常工作。可以为你的 Deno 项目创建一个目录并在该目录下创建一个 index.ts 文件放入以下代码:
function hello(str: string) {
return `Hello ${str}!`;
}
console.log(hello("Deno"));
在 deno run 子命令后将文件名做为参数执行,如果输出 “Hello Deno!”,意味着你已经成功的安装了 Deno。
$ deno run index.ts
Check file:///home/ayo/dev/deno/index.ts
Hello Deno!
要了解 Deno CLI 提供的其它功能及选项,使用 --help 标志:
$ deno --help
Deno 相较于 Node.js 的一大卖点是它对 TypeScript 的一流支持。
正如你所看到的,除了安装 Deno CLI 之外,你不需要做任何的事情让它来工作。与其前身一样,使用 V8 引擎运行时来解析解释和执行 JavaScript 代码,但它还在其可执行文件中使用 TypeScript 编译器以实现对 TypeScript 的支持。
在后台,TypeScript 代码被检查和编译。生成的 JavaScript 代码会缓存在文件系统上的一个目录中,可以再次执行而无需从头编译。你可以使用 deno info 检查缓存目录位置和其它包含 Deno 管理文件的目录。
在使用 TypeScript 时 Deno 不需要任何配置,但如果你想调整 TypeScript 编译器解析代码的方式,你可以提供一个 JSON 文件指定 TypeScript 的编译选项。尽管 tsconfig.json 是用 TSC 编译时的一个常见选择方式,但是 Deno 团队建议使用 deno.json 因为其它特定于 Deno 的配置选项也可以放置在这里。
请注意 Deno 不支持所有的 TypeScript 编译选项。在 Deno 的文档中提供了一个完整的可用的的选项和它们的默认值。对于 Deno 的示例配置文件如下所示:
{
"compilerOptions": {
"checkJs": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noUncheckedIndexedAccess": true
}
}
在撰写本文时,Deno 不会自动检测 deno.json 文件,因此必须通过 --config 标志指定它。然后,这个功能计划在未来的一个版本发布。
$ deno run --config deno.json index.ts
当 Deno CLI 遇到类型错误时,它会停止脚本编译并以非 0 状态码退出终止。你可以通过以下方式绕错错误:
在错误发生的地方使用 //@ts-ignore 或 //@ts-expect-error // @ts-nocheck 在一个文件的开始忽略所有的错误。
Deno 还提供了一个 --no-check 标志来完全禁用类型检查。这有助于防止 TypeScript 编译器在快速迭代问题时减慢你的速度。
$ deno run --no-check index.ts
Deno 以成为 JavaScript 和 TypeScript 的安全运行时而骄傲。它维护安全性部分的方式是通过权限功能完成的。为了演示权限功能在 Deno 中的工作方式,添加以下脚本到您的 index.ts 文件。这个脚本会从 disease.sh 获取最新的全球 Covid-19 统计数据。
async function getCovidStats() {
try {
const response = await fetch("https://disease.sh/v3/covid-19/all");
const data = await response.json();
console.table(data);
} catch (err) {
console.error(err);
}
}
getCovidStats();
当你尝试执行脚本时,它应该显示 PermissionDenied 错误:
$ deno run index.ts
PermissionDenied: Requires net access to "disease.sh", run again with the --allow-net flag
# PermissionDenied:需要对 “disease.sh” 的网络访问,使用 --allow-net 标志再次运行。
上面的错误消息表明脚本没有给予网络访问权限,它建议在命令中包含 --allow-net 标志以授予访问权限。
$ deno run --allow-net index.ts
┌────────────────────────┬───────────────┐
│ (idx) │ Values │
├────────────────────────┼───────────────┤
│ updated │ 1633335683059 │
│ cases │ 235736138 │
│ todayCases │ 32766 │
│ deaths │ 4816283 │
│ todayDeaths │ 670 │
│ recovered │ 212616434 │
│ todayRecovered │ 51546 │
│ active │ 18303421 │
│ critical │ 86856 │
│ casesPerOneMillion │ 30243 │
│ deathsPerOneMillion │ 617.9 │
│ tests │ 3716763329 │
│ testsPerOneMillion │ 473234.63 │
│ population │ 7853954694 │
│ oneCasePerPeople │ 0 │
│ oneDeathPerPeople │ 0 │
│ oneTestPerPeople │ 0 │
│ activePerOneMillion │ 2330.47 │
│ recoveredPerOneMillion │ 27071.26 │
│ criticalPerOneMillion │ 11.06 │
│ affectedCountries │ 223 │
└────────────────────────┴───────────────┘
你可以提供一个以逗号分隔的主机名或 IP 地址的允许列表做为 --allow-net的参数,以便脚本仅可以访问指定的网站,而不是授予脚本访问所有网站的权限(如上所示)。如果脚本尝试链接不在允许列表中的列,Deno 将会阻止它的链接,并且脚本将会执行失败。
$ deno run --allow-net='disease.sh' index.ts
这个功能是 Deno 对 Node.js 的改进之一,任何脚本都可以通过网络访问任何资源。读取和写入文件系统也存在类似的权限。如果一个脚本需要执行任一任务,你需要分别指定 --allow-read 和 --allow-write 权限。两个标志允许你为脚本设置特定可访问的目录,以便文件系统的其它部分免受篡改。Deno 还提供了一个 --allow-all 标志,如果需要,可以为一个脚本开启所有的安全敏感功能。
Deno 的主要目标之一是尽可能与 Web 浏览器兼容。这反映在它使用 Web 平台 API,而不是为某些操作创建特定于 Deno 的 API。例如,我们在上一节中看到了 Fetch API 的实际应用。这是一个在浏览器中使用的确切 Fetch API,在必要时有一些偏差以说明 Deno 中独特的安全模型(这些更改大多无关紧要)。
Deno 的在线文档中列出了所有已实现的浏览器 API。
Deno 管理依赖方式可能是它与 Node.js 的最大不同之处。
Node.js 使用 npm 或 yarn 之类的包管理器将第三方包从 npm 注册表下载到 node_modules 目录和 package.json 文件以跟踪项目的依赖关系。Deno 摒弃了这些机制,转而采用更加以浏览器为中心的方式来使用第三方包:URLs。
这里有一个使用 Deno web 应用程序框架 Oka 来创建基础 web server 的示例:
import { Application } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
app.use((ctx) => {
ctx.response.body = "Hello Deno!";
});
app.addEventListener("listen", ({ hostname, port, secure }) => {
console.log(`Listening on: http://localhost:${port}`);
});
await app.listen({ port: 8000 });
Deno 使用 ES 模块,与 Web 浏览器中使用的模块系统相同。只要引用的脚本(模块)导出方法或其它值,就可以从绝对路径或相对路径导入模块。值得注意的是,文件扩展名必须始终存在,无论您是从绝对路径还是相对路径导入。
虽然您可以从任何 URL 导入模块,但许多专门为 Deno 构建的第三方模块都通过 **deno.land/x** 进行缓存。每次发布模块的新版本时,它都会自动缓存在该位置并使其不可变,以便模块的特定版本内容保持不变。
假设您运行上一个片段中的代码。在这种情况下,它将下载模块及其所有依赖项并将它们缓存在本地 DENO_DIR 环境变量指定的目录中(默认应为 $HOME/.cache/deno)。下次程序运行时,将不会再次下载,因为所有依赖项都已缓存在本地。这类似于 Go 模块系统的工作方式。
$ deno run --allow-net index.ts
Download https://deno.land/x/oak/mod.ts
Warning Implicitly using latest version (v9.0.1) for https://deno.land/x/oak/mod.ts
Download https://deno.land/x/oak@v9.0.1/mod.ts
对于生产应用程序,Deno 创建者建议检查您的依赖性已进入代码版本控制系统,以确保持续可用(即时模块的源代码不可用,无论处于何原因)。将 DENO_DIR 环境变量指向项目中的本地目录(例如 vendor),您可以将其提交给 Git。
例如,下面的命令会将脚本的所有依赖项下载到项目中的 vendor 目录中。随后,您可以提交该文件夹以在您的生产服务器中一次将其全部拉下。您还需要将 DENO_DIR 变量设置为从服务器上的 vendor 目录中读取,而不是重新下载它们。
$ DENO_DIR=$PWD/vendor deno cache index.ts # Linux and macOS
$ $env:DENO_DIR="$(get-location)\vendor"; deno cache index.ts # Windows PowerShell
Deno 还支持对依赖项进行版本控制,以确保可重现的构建。目前,我们已经从 https://deno.land/x/oak/mod.ts 导入了 Oak。这始终会下载最新版本,将来可能与您的程序不兼容。当你第一次下载模块时,它还会导致 Deno 产生警告:
Warning Implicitly using latest version (v9.0.1) for https://deno.land/x/oak/mod.ts
最佳实践是引用一个特定版本,如下所示:
import { Application } from 'https://deno.land/x/oak@v9.0.1/mod.ts';
如果您在代码库的许多文件中引用一个模块,升级它可能会变得乏味,因为您必须在许多地方更新 URL。为了规避这个问题,Deno 团队建议将您的外部依赖项导入到一个集中的 deps.ts 文件中,然后重新导出它们。这是一个 deps.ts 示例文件,它从 Oak 库中导出我们需要的内容。
export { Application, Router } from "https://deno.land/x/oak@v9.0.1/mod.ts";
然后在您的应用程序代码中,您可以按如下方式导入它们:
import { Application, Router } from "./deps.ts";
此时,更新一个模块变得很简单,只需将 deps.ts 文件中的 URL 更改为指向新版本即可。
Deno 提供了标准库(stdlib)旨在成为 Go 标准库的松散端口(译者注:这里原句为 “aims to be a loose port of Go's standard library”)。标准中包含的模块由 Deno 团队审核并随着每一次 Deno release 而更新。提供标准库的目的是让您立即创建有用的 Web 应用程序,而不需要任何第三方包(这是 Node.js 生态系统中的规范)。
你可能会发现一些有帮助的第三方模块,示例如下:
这是一个示例(取自 Deno 官方文档)它取自 Deno 标准库中的 HTTP 模块用于创建一个基本的 Web Server。
import { listenAndServe } from "https://deno.land/std@0.109.0/http/server.ts";
const addr = ":8080";
const handler = (request: Request): Response => {
let body = "Your user-agent is:\n\n";
body += request.headers.get("user-agent") || "Unknown";
return new Response(body, { status: 200 });
};
console.log(`HTTP webserver running. Access it at: http://localhost:8080/`);
await listenAndServe(addr, handler);
通过以下命令开启这个服务:
$ deno run --allow-net index.ts
HTTP webserver running. Access it at: http://localhost:8080/
在不同终端,通过以下命令访问这个正在运行的服务。
$ curl http://localhost:8080
Your user-agent is:
curl/7.68.0
请注意,标准库中的模块当前标记为不稳定(如版本号所示)。这意味着您不应该仅仅依赖它们来进行严肃的生产应用程序。
不可否认 Node.js 成功的主要原因是可以在项目下载使用大量的 NPM 包。如果你正在考虑切换到 Deno,你也许想知道是否要放弃所有你知道且喜爱的 NPM 包。
最简单的答案是 “不”。如果某些 NPM 包依赖于 Node.js API,您可能无法在 Deno 中使用它们(特别是如果 Deno 的 Node.js 兼容层不支持特定 API),但许多 NPM 包可以通过 CDN 在 Deno 中使用,例如 esm.sh 和 skypack.dev。这两个 CDN 都将 NPM 包作为 ES 模块提供,即使包的作者没有专门针对 Deno 进行设计,也可以随后在 Deno 脚本中使用这些包。
这是一个在 Deno 脚本中从 Skypack 到入 NPM 包的示例:
import dayjs from "https://cdn.skypack.dev/dayjs@1.10.7";
console.log(`Today is: ${dayjs().format("MMMM DD, YYYY")}`);
$ deno run index.ts
Today is: October 05, 2021
为确保 Deno 可以发现与包关联的类型,请确保在使用 Skypack CDN 时在包 URL 的末尾添加 ?dts后缀。这会使 Skypack 设置 X-TypeScript-Types 标头,以便 Deno 可以自动发现与包关联的类型。Esm.sh 默认包含此标头,但您可以通过在包 URL 末尾添加 ?no-check 后缀来选择退出。
import dayjs from "https://cdn.skypack.dev/dayjs@1.10.7?dts";
Deno CLI 附带了几个有价值的工具,可以提高开发人员的使用体验。与 Node.js 一样,它带有一个 REPL(交互式解析器),您可以使用 deno repl 访问它。
$ deno repl
Deno 1.14.2
exit using ctrl+d or close()
> 2+2
4
它还有一个内置的文件观察器,可以与它的几个子命令一起使用。例如,您可以使用 --watch 标志将 deno run 配置为在文件更改后自动重建和重新启动程序。在 Node.js 中,这个功能一般是通过一些第三方包来实现的,比如 nodemon。
$ deno run --allow-net --watch index.ts
HTTP webserver running. Access it at: http://localhost:8080/
Watcher File change detected! Restarting!
HTTP webserver running. Access it at: http://localhost:8080/
使用 Deno 1.6,您可以通过 compile 子命令将脚本编译为不需要安装 Deno 的独立可执行文件(您可以在 Node.js 中使用 pkg 执行相同操作)。您还可以通过--target 标志为其他平台(交叉编译)生成可执行文件。编译脚本时,您必须指定其运行所需的权限。
$ deno compile --allow-net --output server index.ts
$ ./server
HTTP webserver running. Access it at: http://localhost:8080/
请注意,通过此过程生成的二进制文件非常庞大。在我的测试中,deno compile 为一个简单的 “Hello world” 程序生成了一个 83MB 的二进制文件。然而,Deno 团队目前正在努力减少文件大小以使其更易于管理。
发布 Deno 程序的另一种方法是通过 bundle 子命令将其打包到单个 JavaScript 文件中。该文件包含程序的源代码及其所有依赖项,可以通过 deno run 执行,如下所示:
$ deno bundle index.ts index.bundle.js
Check file:///home/ayo/dev/demo/deno/index.js
Bundle file:///home/ayo/dev/demo/deno/index.js
Emit "index.bundle.js" (7.39KB)
$ deno run --allow-net index.bundle.js
HTTP webserver running. Access it at: http://localhost:8080/
Deno 附带的另外两个很棒的工具是内置的 linter (deno lint) 和 formatter (deno fmt)。在 Node.js 生态系统中,linting 和格式化代码通常分别由 ESLint 和 Prettier 处理。
使用 Deno 时,您不再需要安装任何东西或编写配置文件来获取 JavaScript、TypeScript 和其他支持的文件格式的 linting 和格式化。
Deno 内置了对 JavaScript 和 TypeScript 代码的单元测试支持。当您运行 deno test 时,它会自动检测任何以 _test.ts 或 .test.ts 结尾的文件(也支持其他文件扩展名)并在其中执行任何定义的测试。
要编写您的第一个测试,请创建一个 index_test.ts 文件并使用以下代码填充它:
import { assertEquals } from "https://deno.land/std@0.109.0/testing/asserts.ts";
Deno.test("Multiply two numbers", () => {
const ans = 2 * 2;
assertEquals(ans, 4);
});
Deno 提供了用于创建单元测试的 Deno.test 方法。它将测试的名称作为其第一个参数。它的第二个参数是测试运行时执行的函数。
还有第二种风格接受一个对象而不是两个参数。除了测试名称和功能之外,它还支持其他属性来配置测试是否应该运行或如何运行。
Deno.test({
name: "Multiply two numbers",
fn() {
const ans = 2 * 2;
assertEquals(ans, 4);
},
});
assertEquals() 方法来自标准库中的测试模块,它提供了一种轻松检查两个值是否相等的方法。
继续运行测试:
$ deno test
test Multiply two numbers ... ok (8ms)
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (37ms)
选择编程语言或环境的主要考虑因素之一是它与编辑器和 IDE 的集成。在 Deno 1.6 中,内置语言服务协议(deno lsp) 被添加到运行时以提供以下功能:
以及任何支持语言服务协议 (LSP) 的编辑器的其他语言智能。你可以在 Deno 的在线文档中了解有关在编辑器中设置 Deno 支持的更多信息。
在本文中,我们考虑了 Deno 运行时的许多方面,以及它是对 Node.js 的升级的方式。
关于 Deno 及其生态系统还有很多话要说,但对于考虑将 Deno 用于新项目的 Node.js 开发人员来说,这应该是一个有用的介绍。Deno 第三方软件包的可用性较低是它明显不足的一个方面,因为它在现实世界中由于年龄太小而没有像 Node.js 那样经过实战测试(Deno 1.0 于 2020 年 5 月发布)。
比较 Node.js 和 Deno 之间的性能可以发现,在大多数情况下,它们在同一个范围内,尽管在少数情况下 Node.js 表现出更出色的性能。随着 Deno 变得更加成熟,所测得的差异势必会有所改善。
在选择 Node.js 和 Deno 时,同样重要的是要记住,Deno 提供的一些好处也可以在第三方包的帮助下在 Node.js 中使用。因此,如果您对 Deno 只有一两点欣赏,那么您很可能在 Node.js 中也可获得类似的结果,尽管不是那么无缝。
译者:五月君
作者:ayooluwa-isaiah
https://blog.appsignal.com/2022/02/09/an-introduction-to-deno-is-it-better-than-nodejs.html
本文题目:Deno:它比Node.js更好吗?
路径分享:http://www.shufengxianlan.com/qtweb/news28/275378.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联