Deno,第一种方法
免责声明
引言与架构
生态系统与初步发展
生产与未来
社区与贡献
免责声明
在开始之前,务必记住,截至撰写本文时,Deno 仍在开发中。因此,由于 API 可能存在未预料到的变更,任何生成的代码都应视为不稳定版本。
因此,我们将以版本号0.21.0作为下一步操作的基础。
最后还应该指出的是,Deno 的目的并非取代 Node 或与其合并。
引言与架构
Deno 是一个跨平台运行时环境,它基于Google's V8引擎,使用Rust语言开发,并使用事件循环系统Tokio库构建。
Node 的问题:
Deno 由其创建者Ryan Dahl ( @ry )于 2018 年 6 月在欧洲 JSConf 大会上发布,距离首次提交仅一个月。
在这次演讲中,Dahl 指出了 Node 架构中的十个缺陷(他对此负有责任)。总结如下:
- Node.js 的发展以回调函数的引入为代价,牺牲了 V8 早期版本中存在的 Promise API。
- 应用程序上下文的安全性
- GYP(生成你的项目)编译系统强制用户编写自己的绑定(Node 和 V8 之间的链接),
C++而 V8 本身不再使用它。 - 依赖管理工具 NPM 与 Node 系统密不可分
require。迄今为止,NPM 模块都存储在一个集中式服务中,并由一家私营公司管理。最终,该package.json文件过于关注项目本身,而忽略了技术代码本身(许可证、描述、仓库等)。 - 随着时间的推移,文件
node_modules夹变得越来越庞大和复杂,导致模块解析算法变得复杂。更重要的是,上述做法node_modules与require浏览器制定的标准存在偏差。 - 文件语法
require省略了.js扩展名,这一点与浏览器标准不同,就像上一点一样。此外,模块解析算法必须浏览多个文件夹和文件才能找到所需的模块。 index.js在 require 能够支持该package.json文件之后,名为“入口点”的函数就失效了。- 浏览器中缺少该
window对象,从而阻止了任何同构。
最后,总体上的缺点是,随着时间的推移,Node 降低了 I/O 事件饱和系统的优先级,而模块系统却从中受益。
Deno的解决方案:
随后,Dahl开始在Deno工作,旨在解决Node的大部分问题。为了实现这一目标,该技术基于一套规则和范式,使未来的开发能够遵循这些指导原则:
-
原生 TypeScript 支持
对于对 TypeScript 语言有着浓厚兴趣的顶级开发者来说,Deno 无疑是一个理想的选择。多年来,我们目睹了 Node.js 在维护新功能
V8和ECMAScript特性的同时,还要避免破坏现有 API 的困境。Deno
的出现彻底改变了这一切,它让你无需对应用程序进行任何初始配置即可立即使用 TypeScript。虽然其使用仅限于默认编译器的原生配置,但你仍然可以通过 `--no-type` 标志将 tsconfig.json 文件传递给编译器--config=<file>。 -
ECMAScript通过支持模块语法和禁止require()函数来实现与 Web 的同构如上所述,Node 存在依赖关系解析效率低下的问题;Deno 通过更加明确、简单和直接的方式解决了这个问题,同时又符合标准。
(import * as log from "https://deno.land/std/log/mod.ts";) -
远程代码被检索并缓存到本地。
与 Deno 类似
node_modules,项目正常运行所需的依赖项会被下载并本地获取。但是,它们不会存储在项目级别,而是存储在 Deno 的全局缓存文件夹中(~/.deno/src默认情况下) 。
无论有多少个本地项目需要同一个依赖项,都不需要重新下载相同版本的依赖项。请注意,此功能与 Deno 类似yarn plug'n'play。 -
最终用户必须明确授予特定权限。
如今,安全性是所有应用程序的基石。为此,Deno 将可执行文件置于沙箱模式下,任何在执行上下文之外的操作都必须经过授权。例如,网络访问权限必须由用户在命令行界面 (CLI) 中明确输入“yes”或使用相应
--allow-net标志来授予。Deno
再次强调,它希望更贴近 Web 的理念(例如,通过网站访问网络摄像头)。 -
一份可交付成果,一个可执行文件
为了确保高效分发,Deno 提供了自己的打包器(
deno bundle),在交付时创建一个单独的可消费文件(.js),之后创建一个单独的可执行二进制文件(deno compile)。 -
最后但并非最不重要的...
Deno 的目标还包括:在出现未处理的错误时始终终止程序;生成与当前浏览器兼容的 JavaScript 代码;在应用程序的最高级别支持 Promise(
top-level awaitV8 支持,TypeScript 端支持等待);能够以高效的速度通过 HTTP 提供服务(即使速度不比 Node 快)。
Deno 完全不针对的目标:
-
使用
package.json类似清单如果代码能够自行获取依赖项,则不需要依赖项管理清单。
-
使用包管理器,例如
npm出于同样的原因,
npm(或等效项)对于 Deno 应用程序的开发并非也不应该是必不可少的。 -
Deno/Node 同构
即使这两种技术使用相同的语言,但它们的设计并不相同,因此不允许同构代码。
建筑模型:
锈
Rust 是用于封装 V8 引擎的语言。它通过一个可在 JavaScript 中使用的 API 公开了各个独立功能。这个名为 `deno -cli` 的链接或绑定libdeno,独立于 Deno 的其他基础架构,以原样交付。这要归功于一个名为 `deno-cli` 的 Rust 模块deno-core(一个crate;https://crates.io/crates/deno),该模块由命令行工具 deno-cli 使用。
如果您愿意,也可以在自己的 Rust 应用程序中使用此 crate。
这deno-cli是 crate 核心、TypeScript 编译器(热编译和最终代码缓存)和 Tokyo(一个事件循环库)之间的链接。
东京
这个用 Rust 编写的库赋予了 Rust 异步编程和面向事件编程的能力。Rust
本身并不支持事件循环管理,因此在 2014 年之前,它一直使用libuvlibuv 库来实现异步和跨平台的 I/O 操作,从而弥补了这一缺陷。
值得注意的是,Node 至今仍在 V8 进程中使用 libuv。
因此,Tokio 成为了 Rust 中所有异步事件驱动编程的参考库。
从 Deno 的角度来看,Tokio 因此负责并行化隔离区deno-core(提醒一下,隔离区deno-core是独立的 Rust crate)中公开的 V8 绑定执行的所有异步 I/O。
V8
最后,正如前文多次提到的,整个架构基于 JavaScript 解释引擎。它会定期更新,以满足最新版本 TypeScript 等的需求。截至撰写本文时,Deno 使用的版本为7.9.3042019 年 10 月 14 日的版本。
生态系统与初步发展
安装 :
目前已有多个版本可通过 Scoop(Windows 系统)和 Homebrew(OS X 系统)安装 Deno。此外
,用户还可以通过cURLShell 手动安装(目前 Linux 系统仅支持此安装方式),或通过iwrPowerShell(Windows 系统)进行安装。
秉承代码的理念,Deno 以单个可执行文件的形式提供。
# Shell
curl -fsSL https://deno.land/x/install/install.sh | sh
# PowerShell
iwr https://deno.land/x/install/install.ps1 -useb | iex
# Scoop
scoop install deno
# Homebrew
brew install deno
安装完成后,运行命令deno https://deno.land/welcome.ts测试其是否正常运行。
deno-cli
命令行界面提供了一系列集成功能,让您能够完全沉浸在 Deno 的专有开发环境中。更重要的是,它还能帮助您在向社区提供库时,确保符合相关标准。
以下是当前可用的命令列表:
deno info允许从程序的入口点检查其依赖关系。deno fmt允许使用集成方式格式化代码。Prettierdeno bundle如前所述,这允许将我们的应用程序及其依赖项转译成一个单独的可交付.js文件(浏览器可以使用的文件)。deno install允许通过 URL 或本地代码将 Deno 应用安装到主文件夹(~/.deno/bin默认)。deno types允许为开发生成 Deno 的 TypeScript 类型deno test允许执行集成测试工具。(Deno 集成了自己的测试库)deno completions允许在终端中添加自动补全功能(通常在 Deno 安装过程中已经添加)。deno eval允许 Deno 解释包含可执行代码的文件或字符串deno xeval(名称与 相同xargs)允许deno eval运行代码,但通过获取来自 的每一行stdin
"HelloWorld.ts"
现在我们来谈谈第一个程序。目前,尽管 Deno 生态系统本身提供了一系列可以在命令行中使用的开发工具,但 VSCode 扩展目录(或其他编辑器)的功能仍然非常有限。因此,
不要指望在编写最初几行代码时就能获得完整的开发体验。
示例 1:Grep
第一个例子是对 grep 行为的简单复现,重点介绍了 Deno 标准库的导入、使用以及文件和参数的操作。
为了对它们进行分组,可以在一个名为 `dependencies.conf` 的文件中声明依赖关系deps.ts:
import * as path from "https://deno.land/std/fs/path/mod.ts";
export { path };
export { green, red, bold } from "https://deno.land/std/colors/mod.ts";
然后以传统方式导入到其mod.ts(相当于index.jsNode 中的):
import { path, green, red, bold } from "./deps.ts";
Deno 中的“ http ”导入是指在编译时检索 Web 资源。Deno 目前仅支持 HTTP http://、https://HTTPS 和file://HTTPS 协议。
然后,我们验证传递的参数以及直接从Deno全局对象检索到的参数:
if (Deno.args.length != 3) {
if (Deno.args.length > 3) {
throw new Error("grep: to much args.");
} else {
throw new Error("grep: missing args.");
}
}
const [, text, filePath] = Deno.args;
最后,我们解析并遍历文件,找出包含您要查找的模式的行:
try {
const content = await Deno.readFile(path.resolve(Deno.cwd(), filePath));
let lineNumber = 1;
for (const line of new TextDecoder().decode(content).split("\n")) {
if (line.includes(text)) {
console.log(
`${green(`(${lineNumber})`)} ${line.replace(text, red(bold(text)))}`
);
}
lineNumber++;
}
} catch (error) {
console.error(`grep: error during process.\n${error}`);
}
deno grep/mod.ts foo grep/test.txtfoo最后,要启动应用程序,请执行包含模式和test.txt字符串文件的命令。
示例 2:Overkill Gues-A-Number
第二个例子是一个小游戏,目标是根据“更多”或“更少”的线索找出 0 到 10 之间的数字。它突出了第三方框架的使用、React 的导入以及 JSX 的兼容性。
引入第三方几乎与引入标准完全相同:
import Home from "./page.tsx";
import {
Application,
Router,
RouterContext
} from "https://deno.land/x/oak/mod.ts";
import { App, GuessSafeEnum, generate, log } from "./misc.ts";
要导入该.tsx文件,必须使用 React 才能运行整个程序。该page.tsx文件已按如下方式完成:
import React from "https://dev.jspm.io/react";
import ReactDOMServer from "https://dev.jspm.io/react-dom/server";
借助.tsx扩展和 React,我们可以使用 JSX 导出在服务器端渲染的组件,例如:
export default (props: HomeProps = {}) => `<!DOCTYPE html>
${ReactDOMServer.renderToString((
<>
<Home {...props} />
<hr />
<Debug {...props} />
</>
))}`;
您可以使用该命令运行此示例。deno guessanumber/mod.ts
最后,您可以在 Github 上找到完整的示例,甚至可以直接从其“raw.githubusercontent” URL 运行它们。
(https://github.com/bios21/deno-intro-programmez)
生产与未来
目前,Deno 尚未达到生产就绪状态。它的主要用途是创建命令行工具、后台任务管理器或 Web 服务器(类似于 Node),但其性能尚未达到 Dahl 的预期水平。
不过,可以开始尝试开发一些内部工具,例如批处理脚本。您可以在https://deno.land/benchmarks.html
上找到实时基准测试结果。
每次提交后,基准测试都会更新,并在多个层面上比较 Deno 与 Node 的性能,例如每秒请求数(这是阻碍生产环境使用的第一个瓶颈)、最大延迟、输入输出交互、内存消耗等等。Deno
在某些方面已经优于 Node,并且随着时间的推移不断改进,希望在所有测试中都名列前茅。
v1.0
除了性能之外,Deno 还为 1.0 版本发布了一系列基本功能和工具,完善了开发者体验,这些功能和工具可以被认为是已准备好用于生产环境的。
调试
目前无法调试或检查应用程序,这在开发过程中会造成诸多限制。这项重要功能在 1.0 版本中是必需的。
利用此功能V8,调试将依赖于V8InspectorClientChrome开发者工具,从而可以使用与任何其他 JavaScript 开发相同的工具。
API稳定化
API 中存在一些 bug,可能出自 TypeScript 层,也可能出自其他层deno-core。这些 bug 虽然不大,但仍然影响着整体的稳定性。
稳定性不仅意味着流畅的执行,还意味着一致且统一的入口点。因此,一些函数的名称甚至签名都需要重新审查。
清晰明确的文档
任何在后台启动的项目都会面临一个常见问题——Deno 的文档仍然非常简略,缺乏用例或针对特定主题的解释。
官方网站目前正在重新设计,很快就会完成。
未来
与第一个稳定版本分离后,将对 CLI 进行添加,提供对添加原生功能的支持(通过 Rust 中称为“ops” crate 的模块),以及在许多其他方面,与 Web 世界和ECMA 标准更加紧密地兼容(例如,通过支持WebAssembly 模块)。
关于命令行界面 (CLI),以下是计划功能的部分列表:
deno compile允许将其整个应用程序编译成一个完全独立的二进制文件。deno doc允许生成包含完整代码文档的 JSON 结构。该 JSON 将成为 Deno 的标准格式,并可供支持该标准的文档可视化工具使用。deno ast允许从给定的入口点生成代码抽象语法树 (AST)的JSON 结构。AST 可供诸如分析工具之类的工具使用ESLint,以编程方式分析代码结构,并识别潜在的代码缺陷或内存泄漏等问题。deno lint结合其他功能,deno fmt可以确保所有开发者编写的代码保持一致,并通过确保代码符合 Deno 标准来提高代码质量。请注意,目前代码检查器配置不可访问或修改。
1.0 版本即将发布,快速的开发进度使得团队能够预计在年底或明年一月初发布。
需要注意的是,Deno 仍然是一个开源的社区项目,社区成员需要通过试验这项技术、探索其极限以及向开发者提供尽可能多的数据来帮助其发展。
社区与贡献
由于 Deno 发展历史尚短,其社区规模仍然较小。尽管如此,它正日渐壮大,越来越多的 Rust 或 Node 开发者对这项技术产生了浓厚的兴趣。
目前最大的 Deno 社区分别位于波兰(其主要贡献者之一是Bartek Iwańczuk ( @biwanczuk ))、韩国、中国和日本。
因此,各地也陆续成立了类似的 Meetup 小组,例如Deno Poland ( @denopoland )和Denoland Korea ( @denoland_kr )。
法国也不甘示弱,已经拥有了第一个小组Paris Deno ( @ParisDeno )。此外,您还可以访问https://deno.news
获取 Deno 的新闻简报。
从贡献的角度来看,还有很多工作要做。在官方仓库提交 Pull Request 非常简单,因为缺失的功能和 bug 列表可以在https://github.com/denoland/deno/milestone找到。此外,贡献规则也已为此编写完成。
TypeScript 层由一个core标准deno_std库(https://deno.land/std/README.md)和一组第三方库组成,这些库被合并到一个目录中,以简化 URL(https://deno.land/x/)。
对标准库和核心库的贡献必须遵守相关规则,但第三方库则不受此限制。
您也可以在开发工具层面做出贡献。事实上,要达到舒适高效的开发体验,还有很多不足之处,例如缺少 VSCode 扩展或等效的测试库Jest(fast-check无论是移植的、同构化的还是重写的)。Deno
需要您的贡献,欢迎提交您的内容;许多提供的库都是从 Node、Rust 甚至 Go 等现有库移植而来。
总之,Deno 目前仍处于早期阶段,但 Ryan Dahl 并非初次尝试。
得益于 1.0 版本的新功能、TypeScript 的易用性、日益精彩的性能表现,以及最重要的——充满活力且不断壮大的社区,Deno 无疑将成为 2020/2021 年最具潜力的热门技术之一。
敬请期待!
