Node.js 的创建者 Ryan Dahl 花了一年半的时间研究 Deno,这是一个新的 JavaScript 运行时环境,可以解决Node 的所有问题。

不要误解我的意思,Node 本身就是一个很棒的服务器端 JavaScript 运行时环境,主要是因为它拥有庞大的生态和 JavaScript。然而,Dahl 承认他应该考虑更多的东西:安全性、模块和依赖性等。

并不是说 Dahl 认为这个平台在短时间内会增长多少。不过早在 2009 年,JavaScript 仍然是这种奇怪的小众语言,每个人都在取笑它,而且还确实许多功能。

What Is Deno?

什么是Deno,它的主要特点是什么?

Deno 是一个基于 V8 构建的安全的 Typescript 运行时,V8 是 Google 的 JavaScript 运行时引擎。

它由以下技术构建:

  • Rust(Deno的核心是用Rust编写的,Node用C ++编写)
  • Tokio(用Rust编写的事件循环)
  • TypeScript(Deno 支持 JavaScript 和开箱即用的 TypeScript)
  • V8(google 在 Chrome 和 Node 等中使用的 JavaScript 运行时)

那么让我们来看看 Deno 提供的功能。

安全性(权限)

Deno 最重要的功能之一就是注重安全性。

与 Node 相反,Deno 默认在沙箱中执行代码,这意味着运行时无权访问:

  • 文件系统
  • 网络
  • 执行其他脚本
  • 环境变量

我们来看看权限系统的工作原理。

(async () => {
    const encoder = new TextEncoder();
    const data = encoder.encode('Hello world\n');

    await Deno.writeFile('hello.txt', data);
    await Deno.writeFile('hello2.txt', data);
})();

该脚本创建了两个名为 hello.txthello2.txt 的文本文件,其中包含一个 “Hello world” 消息。该代码正在沙箱中执行,因此它无法访问文件系统。

还要注意,我们用的是 Deno 命名空间而不是 fs 模块,就像在 Node 中一样。 Deno 命名空间提供了许多基本的辅助函数。如果使用命名空间,就会失去浏览器兼容性,稍后将对此进行讨论。

这样运行它:

deno run write-hello.ts

将会提示以下内容:

Deno requests write access to "/Users/user/folder/hello.txt". Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)]

实际上我们会被提示两次,因为来自沙箱的每次调用都必须请求许可。当然,如果我们选择 allow always 选项就只会被问一次。

如果选择 deny 选项,将抛出 PermissionDenied 错误,而且由于我们的代码中没有任何错误处理逻辑,因此将终止该进程。

如果用以下命令执行脚本:

deno run --allow-write write-hello.ts

会发现没有提示,两个文件都已被创建。

除了文件系统的 --allow-write 标志外,还有 --allow-net--allow-env--allow-run标志用来启用网络请求、访问环境、以及运行子进程。

模块

Deno 就像浏览器一样,通过URL加载模块。很多人起初在看到服务器端带有 URL 的 import 语句时感到困惑,但它确实有意义 —— 只要你能忍受:

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

您可能会问,通过 URL 导入包有什么大不了的?答案很简单:通过 URL,Deno 软件包可以在没有集中注册的情况下进行分发,例如 npm,还有很多问题的解释可以在这里找到。

通过URL导入代码,我们使包的创建者能够在他们认为合适的地方托管自己的代码。 不需要 morepackage.json 和 node_modules。

当启动应用程序时,Deno 会下载所有导入的模块并对其进行缓存。一旦它们被缓存,Deno 将不会再次进行下载,直到通过 --reload 标志特别要求。

这里有几个重要的问题:

如果网站出现故障怎么办?

由于它不是集中式的注册,托管该模块的网站可能会因多种原因而被删除。这取决于它在开发期间的状态——或者更糟糕的是,在生产过程中是有风险的。

正如前面提到过的,Deno 缓存了下载的模块。由于缓存存储在本地磁盘上,Deno 的创建者建议在版本控制系统(即git)中检查它并将其保存在存储库中。这样,即使网站出现故障,所有开发人员都可以访问下载的版本。

Deno 将缓存存储在 $DENO_DIR 环境变量下指定的目录中。如果不自己设置变量,它将被设置为系统的默认缓存目录。可以在本地存储库中的某处设置 $DENO_DIR 并将其签入版本控制系统。

我是否必须一直通过URL导入?

不断输入网址将非常繁琐。值得庆幸的是,Deno 为我们提供了两种选择避免这样做。

第一个选项是从本地文件重新导被出导入的模块,如下所示:

export { test, assertEquals } from "https://deno.land/std/testing/mod.ts";

假设上面的文件名为 local-test-utils.ts。现在,如果想再次使用 testassertEquals 函数,可以像这样引用它:

import { test, assertEquals } from './local-test-utils.ts';

因此,是否从 URL 加载它并不重要。

第二个选项是创建一个导入映射,可以先在 JSON文 件中指定:

{
   "imports": {
      "http/": "https://deno.land/std/http/"
   }
}

然后导入它:

import { serve } from "http/server.ts";

为了使它工作,必须通过包含 --importmap 标志告诉 Deno 导入映射:

deno run --importmap=import_map.json hello_server.ts

那么包版本控制呢?

包提供程序必须支持版本控制,但从客户端来看,只需在 URL 中设置版本号即可,如下所示:https://unpkg.com/liltest@0.0.5/dist/liltest.js

浏览器兼容性

Deno 旨在与浏览器兼容。从技术上讲在使用ES模块时,可以不必借助任何构建工具(如 webpack)来使我们的程序可以在浏览器中使用。

但是,像 Babel 这样的工具会将代码转换为 ES5 版本的 JavaScript,因此,即使在不支持该语言所有最新功能的旧版浏览器中,代码也可以运行。但这也是以在最终文件中包含大量不必要的代码并使输出文件膨胀为代价的。

由自己决定我们的主要目标是什么,并相应地做出选择。

TypeScript支持开箱即用

Deno 可以无需任何配置文件就能够轻松使用 TypeScript。同时可以用纯 JavaScript 编写程序并执行它们而不会有任何麻烦。

总结

Deno 是 TypeScript 和 JavaScript 新的运行时,是一个有趣的项目,现在已经稳定发展了很长一段时间。但是在被认为能够稳定用于生产环境之前还有很长的路要走。

借助它的分布式方法,它需要从集中式软件包注册表(即npm)中释放 JavaScript 生态系统。

Dahl 说他希望在夏天结束前发布 1.0 版本,所以如果你对 Deno 的未来发展感兴趣,可以访问它的 GitHub