共计 11684 个字符,预计需要花费 30 分钟才能阅读完成。
作者:ROBIN WIERUCH
翻译:疯狂的技术宅
原文:https://www.robinwieruch.de/D…
未经容许严禁转载
Deno 是新的 JavaScript 和 TypeScript 运行时。Node.js 的发明者 Ryan Dahl 于 2020 年公布了 Deno,作为 Node.js 的改良。然而 Deno 不是 Node.js,而是全新的 JavaScript 运行时,同时也反对 TypeScript。与 Node.js 类似,Deno 可用于服务器端 JavaScript,但其目标是打消 Node.js 所犯的谬误。它就像 Node.js 2.0 一样,只有工夫能力通知咱们是否会像 2009 年应用 Node.js 一样去应用它。
为什么会有 Deno
Node(2009)和 Deno(2020)的发明者 Ryan Dahl 公布了 Deno 作为 JavaScript 生态系统的补充。当 Ryan 在会议上第一次发表 Deno 时,他谈到了 Node.js 中的谬误。Node.js 曾经成为 JavaScript 生态中不可或缺的工具,已被数百万人应用,然而 Ryan Dahl 对过后做出的决定感到不满。当初 Ryan Dahl 心愿通过 Deno 解决 Node 的设计缺点。Deno 是由 V8 JavaScript 引擎、Rust 和 TypeScript 实现的用于平安服务器端的 JavaScript 和 TypeScript 全新运行时。
- 语言:JavaScript 和 TypeScript 是 Deno 运行时的第一语言。无论你用哪种编写 Deno 程序,仅须要一个文件扩展名即可。只管 TypeScript 一直受到欢送,但具备一流 TypeScript 反对的 Deno 可能是这种趋势的适合答案。
- 兼容性:Deno 尝试与 Web 兼容——这意味着 Deno 程序应该能够在 Deno 和浏览器中运行。毕竟它只是一个可执行的 JavaScript(或 TypeScript)文件,不须要过多关注其环境。在思考所有这些兼容性的同时,Deno 心愿通过应用古代 JavaScript 和 TypeScript 性能来确保可能面向未来。
- 安全性:默认状况下,Deno 是平安的。除非开发人员容许,否则不会进行文件、网络或环境的拜访。这能够避免 Deno 脚本的歹意应用,这种歹意应用极有可能与 Node 脚本一样多。
- 规范库:Deno 带有规范库,这意味着 Deno 中的应用程序比 Node 程序更自洽,因为 Deno 在 JavaScript 之上具备许多外部工具函数。此外 Deno 带有一些内置工具,可改善开发体验。
以下各节将具体介绍所有这些要点,同时从头开始逐渐实现一个小的 Deno 程序。之后咱们将持续用 Deno 开发实在的 Web 利用。
在 MacOS、Windows 和 Linux 上装置 Deno
有多种办法来设置 Deno 应用程序。对你而言,这取决于你的操作系统和在计算机上安装程序的工具链。例如我在 MacOS 上用 Homebrew 来治理计算机上的程序。对于你来说,可能还有其余抉择,所以你应该从 Deno 网站获取的这个办法列表中为你的计算机应用适当的命令。这些命令应在集成终端或命令行界面中执行:
# Shell (Mac, Linux):
curl -fsSL https://Deno.land/x/install/install.sh | sh
# PowerShell (Windows):
iwr https://Deno.land/x/install/install.ps1 -useb | iex
# Homebrew (Mac):
brew install Deno
# Chocolatey (Windows):
choco install Deno
# Scoop (Windows):
scoop install Deno
# 用 Cargo 从源码构建并装置
cargo install Deno
装置 Deno 后,能够在命令行上验证其装置。你的版本可能比我的版本新,因为就我而言,我装置了 Deno 的第一个发行版本 1.0.0。然而以下各节将假设你装置了最新的 Deno 版本:
Deno --version
-> Deno 1.0.0
如果要降级 Deno 的版本,能够应用 Deno upgrade
。另外还能够通过命令行执行上面的近程 Deno 程序,来验证 Deno 在你的计算机上是否可能正确运行:
Deno run https://Deno.land/std/examples/welcome.ts
-> Welcome to Deno
这个 Deno 程序只是在你的命令行上输入一段文本。然而它还向你展现了如何通过动静下载和编译 Deno 程序来从近程源执行该程序。如果你无奈在计算机上设置 Deno,请依照 Deno 官方网站 上的装置阐明进行操作。
HELLO Deno
每次咱们学习新的编程语言常识时,都从“Hello World”示例开始。让咱们的第一个 Deno 利用程也从这里开始。在命令行中,为你的 Deno 我的项目创立一个文件夹,进入到该文件夹,并创立一个新文件。你本人决定如何命名文件夹和文件:
mkdir Deno-project
cd Deno-project
touch index.js
而后在你喜爱的编辑器或 IDE 中关上新创建的 index.js 文件。输出以下 JavaScript 代码:
console.log('Hello Deno');
而后在命令行通过以下命令启动 Deno 程序。
Deno run index.js
-> Hello Deno
你的第一个 Deno 程序输入“Hello Deno”。你曾经为 Deno 我的项目创立了一个文件夹,为实现细节创立了一个 JavaScript 文件,并在命令行上通过 Deno 运行了该文件。无需其余设置。
Deno 的权限
以下各节将通过逐渐介绍 Deno 的每个方面,来改良咱们的第一个 Deno 程序。本节将探讨 Deno 中的权限,因为 Deno 在默认状况下是平安的。在本示例中,咱们将理解这到底意味着什么。
如果你想像我一样随时理解技术主题,你可能曾经晓得 Hacker News。你能够在这个网站上浏览无关技术的最新新闻。我喜爱在本人的教程中应用 Hacker News 的 API。为了学习无关 Deno 和权限中的数据获取的常识,咱们将用这个 API 来获取数据。如果浏览 Hacker News API,则可能找到以下 URL 来申请无关某个主题下的文章:
http://hn.algolia.com/api/v1/search?query=...
咱们将在 Deno 我的项目的 index.js 文件中应用此 URL,来获取无关 JavaScript 的 Hacker News 文章:
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
接下来,用 Deno 内置的 fetch 函数解决 URL,该函数在 URL 上执行 HTTP GET 申请,并返回 JavaScript promise。你能够通过将其转换为 JSON 并用日志记录语句输入其后果来解决这个 promise:
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
fetch(url)
.then((result) => result.json())
.then((result) => console.log(result.hits));
如果你用 JavaScript 写过前端程序,则可能曾经留神到,咱们所应用的浏览器 API 为客户端程序提供了雷同的 fetch API(或至多应用雷同实现细节的接口)。如前所述,Deno 尝试与 Web 兼容,并且任何 Deno 程序在执行其代码时都应该可能在浏览器中以雷同的形式工作。因而 Deno 确保客户端 JavaScript 程序中可用的 API 也能够在服务器端 Deno 应用程序中应用。
当初,在命令行上再次启动 Deno:
Deno run index.js
你应该会看到 Deno 提醒的谬误:“Uncaught PermissionDenied: network access to “http://hn.algolia.com/api/v1/search?query=javascript”, run again with the –allow-net flag”。呈现这个谬误的起因是,在默认状况下 Deno 是平安的。如果咱们在 Deno 的域中操作,能够无需授予 Deno 任何许可而做很多事件而。然而如果咱们想超过 Deno 的职责范畴,则须要明确容许它。在这种从近程 API 获取数据的状况下,须要容许网络申请:
Deno run --allow-net index.js
再次运行 Deno 程序后,你应该在命令行上看到一系列 Hacker News 文章。这个数组中的每个我的项目都有许多信息,为了便于浏览,让咱们精简每个我的项目(文章)的属性。之后输入会应更具可读性:
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
fetch(url)
.then((result) => result.json())
.then((result) => {const stories = result.hits.map((hit) => ({
title: hit.title,
url: hit.url,
createdAt: hit.created_at_i,
}));
console.log(stories);
});
在本节中,你理解了 Deno 在默认状况下是平安的。咱们必须容许本人可能拜访 Deno 畛域以外的所有内容,可能是网络拜访或文件拜访,否则 Deno 将会回绝工作。
Deno 的兼容性
后面你曾经看到了怎么在 Deno 中应用 fetch。咱们对浏览器中的 fetch API 是很相熟的。所以在 Deno 中能够用与浏览器端完全相同的接口,而不用为 Deno 应用新的 API。在应用 Deno 时咱们不须要重新考虑本人的办法。
Deno 尝试跟上古代 JavaScript 性能,无论是在客户端还是在服务器上。以 async/await 为例,它仅在较新的 Node.js 版本中可用,默认状况下在 Deno 中是可用的。你不仅能够应用 async/await,而且还能够应用 async 的 top level await(这在 Node.js 中曾经存在很长时间了):
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
const result = await fetch(url).then((result) => result.json());
const stories = result.hits.map((hit) => ({
title: hit.title,
url: hit.url,
createdAt: hit.created_at_i,
}));
console.log(stories);
作为惯例 promise 的 then 和 catch 块的代替,能够用 await 同步运行代码。在 await 语句之后的所有代码仅在 promise 解决后执行。如果这种实现要在函数中运行,则必须把函数申明为异步。开箱即用的 Deno 中提供了 Async/await 和 top level await。
Deno 的规范库
Deno 带有一组实用函数,这些函数被称为 Deno 的规范库(简称:Deno std)。Deno 并没有从内部库中导入所有内容,而是尝试通过提供几种外部解决方案来使其可用。接下来咱们尝试用上面的规范库解决方案之一来设置 Web 服务器:
import {serve} from 'https://Deno.land/std/http/server.ts';
const server = serve({port: 8000});
for await (const req of server) {req.respond({ body: 'Hello Deno'});
}
首先咱们用绝对路径从规范库中进行命名导入。在 Deno 中,所有库导入(无论是从规范库还是从第三方库)均应用指向专用文件的绝对路径来实现。你从这个 以服务器文件模式存在的 http 库 导出一个名为 served
的函数。
serve
函数为咱们创立了一个 Web 服务器,可通过已定义的端口对其进行拜访。JavaScript for await … of 用于遍历每个传入此服务器的申请。对于每个申请,服务器在响应注释中返回雷同的文本。
再次运行你的 Deno 程序,而后在浏览器中导航到 http://localhost:8000。因为要再次应用网络,所以须要受权:
Deno run --allow-net index.js
http://localhost:8000 和带有结尾斜杠的 http://localhost:8000/ 这两个 URL 在浏览器中的工作形式雷同。当在浏览器中关上其中一个 URL 时,都会向 Deno 程序收回 HTTP GET 申请,并且该申请返回带有 Hello Deno
注释的 HTTP 响应,而后该响应将显示在浏览器中。
接下来用后面的代码扩大该示例。咱们不会从服务器(Deno)上将硬编码文本发送回客户端(浏览器),而是从 Hacker News 获取最重要的 JavaScript 文章并将其发送给客户端:
import {serve} from 'https://Deno.land/std/http/server.ts';
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
const server = serve({port: 8000});
for await (const req of server) {const result = await fetch(url).then((result) => result.json());
const stories = result.hits.map((hit) => ({
title: hit.title,
url: hit.url,
createdAt: hit.created_at_i,
}));
req.respond({body: JSON.stringify(stories) });
}
再次启动 Deno 程序后,应该可能看到从 fetch 申请中失去的后果以 JSON 的模式打印在浏览器中。以前咱们只在命令行上看到这个后果,然而当初有了 Web 服务器,能够将后果发送到客户端(浏览器)。
Deno 库
只依赖 Deno 的规范库还不足以创立 Deno 程序,这就须要第三方库(也称为内部库或库)发挥作用了。如果你再次从浏览器的最初一部分中查看后果,可能会留神到 createdAt
的格局对人类很不敌对,咱们将用 date-fns 库来使其可读:
Deno 中的库通过绝对路径间接从 Web 导入。在浏览器中再次关上 URL,并浏览其中的源代码,并查看它是否真的导出了默认函数,即此处的 format
函数:
import {serve} from 'https://Deno.land/std/http/server.ts';
import format from 'https://Deno.land/x/date_fns/format/index.js';
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
const server = serve({port: 8000});
for await (const req of server) {const result = await fetch(url).then((result) => result.json());
const stories = result.hits.map((hit) => ({
title: hit.title,
url: hit.url,
createdAt: format(new Date(hit.created_at_i * 1000),
'yyyy-MM-dd'
),
}));
req.respond({body: JSON.stringify(stories) });
}
format
函数有两个强制性参数:日期和格式化日期的模式。咱们从 Hacker News API 收到的日期是一个 unix 工夫戳,以秒为单位;所以要先把它转换为毫秒,而后再从中创立 JavaScript 日期。为函数第二个参数提供的模式使日期易于浏览。
再次启动 Deno 程序后,你会看到它从库中下载了 format 函数以及所有依赖项。因为应用了函数的间接 URL,所以只下载了库的这一部分。如果试着蕴含整个库门路,将会看到整个库将被下载,format 只是许多命名的导出(大括号)之一:
import {serve} from 'https://Deno.land/std/http/server.ts';
import {format} from 'https://Deno.land/x/date_fns/index.js';
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
const server = serve({port: 8000});
for await (const req of server) {...}
当再次启动 Deno 时,都会应用被缓存的库,所以无需再次下载。它都会查看所有的导入,将其下载并捆绑到一个可执行文件中。在 Deno 中导入库的形式受到 Go 语言 的启发。不用在文件中保留依赖项列表(例如,Node.js 的package.json),也不须要使所有模块在我的项目中可见(例如,Node.js 的 node_modules)。
Deno 的导入
你曾经理解到 Deno 的规范库或第三方库的导入是通过绝对路径执行的。这种办法的灵感来自 Go 语言,因为它不会产生太多混同的空间。因为你的 Deno 程序有多个文件,因而能够用相对路径导入它们。
来看看它是怎么工作的:首先,在我的项目中创立一个名为 stories.js 的文件,该文件应该与 index.js 文件在同一门路下。在 stories.js 文件中,输出以下代码实现,这段代码实质上上是咱们之前在其余文件中所做的映射:
import {format} from 'https://Deno.land/x/date_fns/index.js';
export const mapStory = (story) => ({
title: story.title,
url: story.url,
createdAt: format(new Date(story.created_at_i * 1000),
'yyyy-MM-dd'
),
});
在 stories.js 文件中进行命名导出,并在 index.js 文件中进行命名导入,并在稍后的代码中应用该函数:
import {serve} from 'https://Deno.land/std/http/server.ts';
import {mapStory} from './stories.js';
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
const server = serve({port: 8000});
for await (const req of server) {const result = await fetch(url).then((result) => result.json());
const stories = result.hits.map(mapStory);
req.respond({body: JSON.stringify(stories) });
}
这就是在 Deno 中导出和导入文件的过程。与之前所用的绝对路径不同,咱们用相对路径来导入必要的内容。还要留神的是,无论绝对路径还是相对路径,咱们都必须始终蕴含文件扩展名,因为不能留下任何产生歧义的余地。
在 Deno 中进行测试
在编程的过程中,测试不应该预先再去思考,在 Deno 中也一样,测试是必不可少的。接下来通过编写第一个单元测试来理解其工作原理。首先创立一个新的 stories.test.js 文件。而后编写以下代码:
import {mapStory} from './stories.js';
Deno.test('maps to a smaller story with formatted date', () => {});
Deno 为咱们提供了一个 test
性能,用来定义名称、阐明和理论的测函数。怎么在函数主体中实现测试取决于咱们本人。咱们曾经导入了要测试的函数(即 mapStory
),该函数实际上只接管一个文章列表数组,并返回具备较少属性和格式化日期的新文章数组。咱们须要做的就是定义一个用于 mapStory
的文章列表和一个其假设为该函数的输入的文章列表:
import {assertEquals} from 'https://Deno.land/std/testing/asserts.ts';
import {mapStory} from './stories.js';
Deno.test('maps to a smaller story with formatted date', () => {
const stories = [
{
id: '1',
title: 'title1',
url: 'url1',
created_at_i: 1476198038,
},
];
const expectedStories = [
{
title: 'title1',
url: 'url1',
createdAt: '2016-10-11',
},
];
assertEquals(stories.map(mapStory), expectedStories);
});
Deno 的规范库提供了 assertEquals
函数用来申明两个值。第一个值是要测试的函数的输入,第二个值是预期的输入。如果两者都匹配,则测试应变为绿色。如果它们不匹配,则测试应失败并变为红色。在命令行上运行所有测试:
Deno test
-> running 1 tests
-> test maps to a smaller story with formatted date ... ok (9ms)
-> test result: ok. (10ms)
-> 1 passed;
-> 0 failed;
-> 0 ignored;
-> 0 measured;
-> 0 filtered out
测试变成绿色。用 Deno test
命令将拾取所有具备命名模式 test.{js,ts,jsx,tsx} 的文件。你也能够用 Deno test <file_name>
仅测试特定文件,在本例中为 Deno test stories.test.js
。
在 Deno 中应用 TypeScript
Deno 反对把 JavaScript 和 TypeScript 同时作为第一语言。这就是为什么进行文件导入时要始终蕴含文件扩展名的起因——无论这些文件是从 Deno 我的项目的相对路径导入还是从 Deno 规范库或第三方库绝对路径导入。
因为 Deno 把 TypeScript 作为一等公民提供反对,所以能够把 stories.js
文件重命名为 stories.ts
。你会留神到须要调整所有导入——在 index.js 和 stories.test.js 中指向该文件,因为文件扩展名从 .js 被改为了 .ts。
带有所有实现细节的 stories.ts 文件当初须要类型。咱们将通过 Story
和 FormattedStory
提供函数输出和输入的接口:
import {format} from 'https://Deno.land/x/date_fns/index.js';
interface Story {
title: string;
url: string;
created_at_i: number;
}
interface FormattedStory {
title: string;
url: string;
createdAt: string;
}
export const mapStory = (story: Story): FormattedStory => ({
title: story.title,
url: story.url,
createdAt: format(new Date(story.created_at_i * 1000),
'yyyy-MM-dd'
),
});
当初这个 function 已齐全类型化了。通过将 stories.test.js 文件重命名为 stories.test.ts,并将 index.js 文件重命名为 index.ts,你能够本人持续把 JavaScript 转换为 TypeScript。这些新的 TypeScript 文件并不是都须要增加类型或接口,因为大多数类型是主动推导的。
如果要再次启动 Deno 应用程序,这时必须调整 Deno 脚本的文件扩展名:
Deno run --allow-net index.ts
Deno 带有默认的 TypeScript 配置。如果要自定义它,能够增加自定义 tsconfig.json 文件。毕竟,因为 TypeScript 和 JavaScript 一样,都是一等的公民,所以由你本人决定为未来的 Deno 项目选择哪种文件扩展名。
Deno 中的环境变量
环境变量非常适合暗藏无关 Deno 程序的敏感信息。这能够是 API 密钥、明码或别人不应该看到的数据。这就是咱们要通过创立 .env 文件来暗藏敏感信息的起因。接下来咱们将创立这个文件,并把以下信息传给服务器程序的端口:
PORT=8000
在 index.ts
文件中,咱们能够把这个环境变量与第三方库在一起配合应用:
import {serve} from 'https://Deno.land/std/http/server.ts';
import {config} from 'https://Deno.land/x/dotenv/mod.ts';
import {mapStory} from './stories.ts';
const url = 'http://hn.algolia.com/api/v1/search?query=javascript';
const server = serve({port: parseInt(config()['PORT']),
});
for await (const req of server) {const result = await fetch(url).then((result) => result.json());
const stories = result.hits.map(mapStory);
req.respond({body: JSON.stringify(stories) });
}
config
函数从 .env 文件返回带有有所有键值对的对象。咱们必须将 'PORT'
键的值解析为数字,因为它能够在对象中作为字符串应用。当初该信息不会存在于源代码中,而仅在环境变量文件中可用。
再次启动 Deno 程序后,你应该在命令行上看到另一个权限谬误:“Uncaught PermissionDenied: read access to “/Users/mydspr/Developer/Repos/Deno-example”, run again with the –allow-read flag”。能够用另一个权限标记来容许拜访环境变量:
Deno run --allow-net --allow-read index.ts
重要提醒:.env 文件不应在每个人都能够看到的公共存储库中共享。如果你将源代码公开(例如在 GitHub 上),请思考将 .env 文件增加到 .gitignore 文件中。
毕竟服务器程序的端口不是敏感数据的最好例子。咱们应用端口是为了理解环境变量。然而一旦你解决了 Deno 程序的更多功能,最终可能会失去源代码所中应用的信息,这些信息对于其他人不可见。
总结
本文向你介绍了 Deno 所有的基础知识。从小型脚本到功能完善的服务器利用,Deno 将在与 Node.js 雷同的畛域中应用,但其默认设置会大大改善。在默认状况下,它是权限是平安的,并与许多客户端的 API 兼容,有着诸如 top level await 等古代性能,并反对 JavaScript 和 TypeScript。
本文首发微信公众号:前端先锋
欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章
欢送持续浏览本专栏其它高赞文章:
- 深刻了解 Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13 个帮你进步开发效率的古代 CSS 框架
- 疾速上手 BootstrapVue
- JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
- WebSocket 实战:在 Node 和 React 之间进行实时通信
- 对于 Git 的 20 个面试题
- 深刻解析 Node.js 的 console.log
- Node.js 到底是什么?
- 30 分钟用 Node.js 构建一个 API 服务器
- Javascript 的对象拷贝
- 程序员 30 岁前月薪达不到 30K,该何去何从
- 14 个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩大插件
- Node.js 多线程齐全指南
- 把 HTML 转成 PDF 的 4 个计划及实现
- 更多文章 …