## 1 引言

Deno 是什么？Deno 和 Node 有什么关系？Deno 和我有什么关系？

Deno 将于 2020-05-13 发布 1.0，如果你还有上面的疑惑，可以和我一起通过 [Deno 1.0: What you need to know](https://blog.logrocket.com/deno-1-0-what-you-need-to-know/) 这篇文章一起了解 Deno 基础知识。

希望你带着疑问思考，未来 10 年看今天，会不会出现 Deno 官方生态壮大，完全替代 Node 进而影响到 Web 生态的局面呢？这个思考结果会影响到你未来职业发展，你需要学会自己思考，并对这个思考结果负责。

## 2 介绍 & 精读

Deno 的作者是 Ryan Dahl，他是 Nodejs 背后的策划者，曾经说过 [我对 Nodejs 感到遗憾的 10 件事](https://www.youtube.com/watch?v=M3BM9TB-8yA)。这也是为什么新开一个坑的原因，但 Deno 并不定位为 Nodejs 的替代品，从整体功能来看，Deno 有更大的野心，据我的推测是想要取代现在陈旧的前后端开发模式，让 Deno 一统前后端开发全流程。

Nodejs 是由 C++ 写的，而 Deno 则是由 Rust 写的，并选择了 [Tokio](https://tokio.rs/) 这个异步编程框架，并使用 V8 引擎解析 Javascript，并内置了对 Ts 的解析。

### 安装

Deno 支持如下安装方式：

**Shell:**

```shell
curl -fsSL https://deno.land/x/install/install.sh | sh
```

**PowerShell:**

```shell
iwr https://deno.land/x/install/install.ps1 -useb | iex
```

**Homebrew:**

```shell
brew install deno
```

**Chocolatey:**

```shell
choco install deno
```

脚本执行方式为 `deno run`，可以类比为 `node`，但功能不同且支持远程文件，实际上远程依赖是 Deno 的一大特色，也是有争议的地方：

```shell
deno run https://deno.land/std/examples/welcome.ts
```

在 ts 文件中允许用远程脚本加载资源，这个后面还会提到：

```ts
import { serve } from "https://deno.land/std@v0.42.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}
```

### 安全性

Deno 是默认安全的，这体现在默认没有环境、网络访问权限、文件读写权限、运行子进程的能力。所以如果直接运行一个依赖权限的文件会报错：

```shell
deno run file-needing-to-run-a-subprocess.ts

# error: Uncaught PermissionDenied: access to run a subprocess, run again with the --allow-run flag
```

可以通过参数方式允许权限的执行，有 `--allow-read`、`--allow-write`、`--allow-net` 等：

```shell
deno --allow-read=/etc
```

上面表示 `/etc` 文件夹下的文件拥有文件读权限。

除了直接加参数调用、Bash 脚本调用外，还可以用 Make 运行，或者使用类似的 [drake](https://deno.land/x/drake/) 启动。

或者使用 `deno install` 命令，将脚本转化为一个快捷指令：

```shell
deno install --allow-net --allow-read -n serve https://deno.land/std/http/file_server.ts
```

`-n` 表示 `--name`，可以对这个脚本进行重命名，比如上面的例子中，`serve` 命令就等同于 `deno run --allow-net --allow-read https://deno.land/std/http/file_server.ts`。

### 标准库

Deno 在标准库上很有特点，对常用功能提供了官方版本，保证可用性与稳定性。原文中列出了一些与 Npm 三方库的对比：

| Deno Module | Description                                                                       | npm | Equivalents              |
| ----------- | --------------------------------------------------------------------------------- | --- | ------------------------ |
| colors      | Adds color to the terminal                                                        |     | chalk, kleur, and colors |
| datetime    | Helps working with the JavaScript Date object                                     |     |
| encoding    | Adds support for external data scructures like base32, binary, csv, toml and yaml |     |
| flags       | Helps working with command line arguments                                         |     | minimist                 |
| fs          | Helps with manipulation of the file system                                        |     |
| http        | Allows serving local files over HTTP                                              |     | http-server              |
| log         | Used for creating logs                                                            |     | winston                  |
| testing     | For unit testing assertion and benchmarking                                       |     | chai                     |
| uuid        | UUID generation                                                                   |     | uuid                     |
| ws          | Helps with creating WebSocket client/server                                       |     | ws                       |

从这个点上来看，Deno 既做运行环境又做基础生态，缓解了 Npm 生态下选择困难症，这件事需要辩证来看：集成了官方包对功能确定的模块来说是很有必要的，而且提高了底层库的稳定性；但 Deno 生态也有三方库，而且本质上三方库和官方库在功能上没有任何壁垒，因为实现代码都类似，唯一区别是谁能为其稳定性站台，假设微软和 Deno 同时出了基于 Npm 生态与 Deno 生态官方库，都保证会持续维护，你更相信谁呢？官方是否有优势要取决于官方自身的实力。

### 内置 Typescript

Deno 内置支持了 TS，因此不需要 `ts-node` 我们就可以用 `deno run test.ts` 运行 Typescript 文件。值得注意的是，Deno 内部也是利用 Typescript 引擎解析为 Js 后交由 V8 引擎解析，因此本质上没太大的变化，只是这样 Deno 的生态会更规范。

由于内置了 TS 支持，自然也不需要写 `tsconfig.json` 配置了，但你依然可以定制它：

```shell
deno run -c tsconfig.json [file-to-run.ts]
```

Deno 默认还开启了 TS 严格模式，所以看到这里，可以认为 Deno 是为了构建高质量理想库而诞生的运行环境，基于已有的生态来做，但做了更多内置技术选型，这和 Facebook 的 [rome](https://github.com/facebookexperimental/rome) 很像，但做的却更彻底。

其实从实现上来看，我们基于 Javascript 生态也能写出 `deno run test.ts` 这样类似的引擎，只不过是由 JS 驱动执行，可能编译还会选择 Webpack，但 Deno 本身基于 Rust 实现，并重新实现了一套模块加载标准，可以说从更底层的方式重新解读了 W3C 标准规范，以期望解决 Javascript 生态的各种痛点问题。

### 支持 Web 标准

Deno 还支持 W3C 标准规范，因此像 `fetch`、`setTimeout` 等 API 都可以被直接使用，如果你按照 Deno 支持的那几个函数写代码，可以保证在 Deno、Node、Web 三个平台实现跨平台运行。

虽然距离完全实现 W3C 所有标准规范还有一些路要走，但我们看到了 Deno 兼容规范的决心。

### ESModule

模块化是 Deno 的亮点，Deno 使用官方 ESModule 规范，但引用路径必须加上后缀：

```ts
import * as log from "https://deno.land/std/log/mod.ts";
import { outputToConsole } from "./view.ts";
```

Deno 不需要申明依赖，代码的引用路径就是依赖申明，会包括完整的路径以及文件后缀，也支持网络资源，可以摆脱 NPM 中心化的包管理模式，因为这个路径可以是任何网络地址。

### 包管理

对于 `import * as log from "https://deno.land/std/log/mod.ts";` 这行代码，Deno 会下载到一个缓存文件夹，用户不会感知到这个文件夹与这个过程的存在，也就是说，Deno 环境中是没有 `node_modules` 的。

也可以通过 `deno --reload` 的方式强制刷新缓存。

但这里也要辩证的看待 “Deno 去中心化” 这件事，虽然引用了网络源，但会引发下面几个问题：

1. 实际上还存在一个 "node_modules"，只是用户看不到。
2. 网络下载速度放到运行时，第一次启动还是很慢。
3. 普通模式下无 lock，必须配合 `deps.ts` 使用，这个后面会提到。

即使被打上 “中心化恶人” 的 npm 也有去中心化的一面，因为 npm 支持私有化部署，无论是速度还是稳定性都可以由公司自己掌控，从稳定性来说还是 npm 拥有压倒性优势。

### 三方库

Deno 还有第三方库生态，截止目前共有 [221 个三方库](<[](https://deno.land/x/)>)。

由于 Deno 走网络资源，我们可以借助 [Pika](https://www.pika.dev/cdn) 提供的 CDN 服务直接引用网络资源包：

```jsx
import * as pkg from "https://cdn.pika.dev/preact@^10.3.0";
```

虽然这样看上去很轻量，但对公司来说还是需要自建一个 “Pika” 保障稳定性，以及做全球 CDN 缓存等的工作。

### 告别 package.json

npm 生态下包信息存放在 `package.json`，包含但不限于下面的内容：

- 项目元信息。
- 项目依赖和版本号。
- 依赖还进行分类，比如 `dependencies`、`devDependencies` 甚至 `peerDependencies`。
- 标记入口，`main` 和 `module`，还有 TS 用的 `types` 与 `typings`，脚手架的 `bin` 等等。
- npm scripts。

随着标准的不断更新，`package.json` 信息已经非常臃肿了。

对于 Deno 来说，则使用 `deps.ts` 集中管理依赖：

```ts
export { assert } from "https://deno.land/std@v0.39.0/testing/asserts.ts";
export { green, bold } from "https://deno.land/std@v0.39.0/fmt/colors.ts";
```

`deps.ts` 就是一个普通文件，只是将项目的依赖精确描述出来，这样其他地方引用 `assert` 时，就可以这么写了：

```ts
// import { assert } from "https://deno.land/std@v0.39.0/testing/asserts.ts";
import { assert } from "./deps.ts";
```

如果需要锁定依赖，可以通过 `deno --lock=lock.json` 方式申明。

### deno doc

`deno doc <filename>` 命令可以根据文件按照 JS Doc 规则生成文档，同时也支持 TS 语法，比如下面这段代码：

```ts
/** Asynchronously fulfill a response with a file from the local file
 * system. */
export async function send(
  { request, response }: Context,
  path: string,
  options: SendOptions = { root: "" }
): Promise<string | undefined> {
  // ...
}
```

生成文档如下：

```text
function send(_: Context, path: string, options: SendOptions): Promise<string | undefined>
Asynchronously fulfill a response with a file from the local file system.
```

deno 本身文档就是用这个命令生成的，可以 [访问官方文档](https://doc.deno.land/) 查看使用效果。

### 内置工具链

前端 Javascript 工具链相当混乱，虽然业界已有 Umi 等框架做了开箱即用的封装，但回到 Javascript 设计的初衷就是可以在浏览器直接使用的，包括浏览器对不依赖构建工具的模块化支持，注定了未来 Webpack 一定会被消灭。

Deno 通过内置一套工具链的方式解决这个问题，包括：

- 测试：提供 `deno test` 命令与 `Deno.test()` 测试函数。
- 格式化：提供 [vscode 插件](https://marketplace.visualstudio.com/items?itemName=axetroy.vscode-deno)。
- 编译：提供 `deno bundle` 命令。

不过值得注意的是，在最重要的编译环节，`deno bundle` 目前提供的能力是相对欠缺的，比如还不支持 Tree Shaking。

用 Rust 等语言提升构建效率是业界一直在尝试的事，比如 @陈成 就基于 [esbuild](https://github.com/evanw/esbuild) 做了 [@umijs/plugin-esbuild](https://umijs.org/zh-CN/plugins/plugin-esbuild) 插件用于提升 Umi 构建速度，但为了防止生产构建产物与 Webpack 默认规则不一致，仅使用了其压缩（minifier）功能。

对 deno 来说也一样，目前其实没有任何证据表明 deno 的构建结果可以完美适配 webpack 环境，所以请勿认为 deno 发布了 1.0 版本就等于可以在生产环境使用。

## 3 总结

正如原文结尾所说的，Deno 虽然将要发布 1.0 版本，但仍不能完全替代 Nodejs，这背后的原因主要是历史兼容成本，也就是完整支持整个 Node 生态不只是设计的问题，更是一个体力活，需要一个个高地去攻克。

同样 Deno 对 Web 的支持也让人耳目一新，但仍不能放到生产环境使用，除了官方和三方生态还在逐渐完善外，`deno bundle` 对 Tree Shaking 能力的缺失以及构建产物无法保证与现在的 Webpack 完全相同，这样会导致对稳定性要求极高的大型应用迁移成本非常高。

最亮眼的改动是模块化部分，依赖完全去中心化从长远来看是一个非常好的设计，只是基础设施和生态要达到一个较为理想的水平。

最后，让我们站在一个预言者角度思考一下 Deno 到底会不会火吧：

Deno 做的初心是做一个更好的 Node，但很不幸，对于这种级别的生态底层工具来说，重新做一个并重新火起来的难度，不亚于重新做一个阿里巴巴并取代现在阿里的难度。也就是不同的时间点做同一件事，哪怕后者可以吸取教训，大概率也无法复制以前成功的路线。

从 Deno 的功能来看，解决了 Node 很多痛点，其中就包括去中心化管理，有点云开发的意思，但在 2020 年，基于 Nodejs 和 Webpack 的云开发都搞出来了，说实话是没有 Deno 什么空间的。从功能上来看，开篇就说了 Deno 基于 V8 解析 Javascript，对于性能和功能都没有革命性提升，从技术上作出突破也几乎不可能了。

Deno 的思想确实比 Node 先进，但不能说比 Node 好十倍，则无法撼动 Node 的生态，即便是 Node 作者自己可能也不行。

然而我上面说的可能都是错的。

> 讨论地址是：[精读《Deno 1.0 你需要了解的》 · Issue #248 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/248)

**如果你想参与讨论，请 [点击这里](https://github.com/dt-fe/weekly)，每周都有新的主题，周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**

> 关注 **前端精读微信公众号**

<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">

> 版权声明：自由转载-非商用-非衍生-保持署名（[创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)）
