Deno 常见问题

🌙
手机阅读
本文目录结构

如果 Deno 官网挂了怎么办?

如果 https://deno.land/ 宕机怎么办?

我们写代码时候很多都是依赖官方的服务,比如

deno run --allow-read https://deno.land/std/examples/cat.ts /e/deno/axihe.com.md

依赖外部服务,这种方式在开发的时候非常方便,但在生产环境很脆弱,怎么搞呢?

Deno 给出的方案是:生产级软件总是应该打包所有依赖

在 Deno 中,您应该将 $DENO_DIR 检入版本控制系统,并在运行时指定 $DENO_DIR 环境变量。

Deno 怎么保证 URL 引用的内容一致性?

就像上面一样,我们通过 URL 引用,那么还是有一种风险,就是远程的 URL 代码改变了,我们的项目可能就 BUG 频繁出现了。

可能出现的场景,就是今天我们写好的代码,测试通过了,但是下周另外一位同事把代码拉下来,因为 url 引入的内容改变了,导致项目起不来了。

这是非常容易出现的,Deno 怎么处理这件事情呢?

Deno 给的解释是,使用 --lock 命令行选项,通过一个锁文件 (lock file),您可以确保自己运行的是所期望的代码。

这个就类似 npm 的package.json,npm 的锁文件参考 NPM package-lock.json

更多信息请看 Deno完整性检查与锁定文件

Deno 如何导入特定版本?

如果是官方的,只需在 URL 中指定版本,举个例子,这个 URL 指定了要运行的版本 https://unpkg.com/liltest@0.0.5/dist/liltest.js

结合之前提到的在生产中设置 $DENO_DIR 的方法,您可以完全指定要运行的代码,并且无需访问网络。

Tips: 这种方式还是挺 low 的,只有官方的,可信的才能这么搞。

所有文件都导入 URL 似乎很麻烦

如果其中一个 URL 链接到一个完全不同的库版本,该怎么办?

在大型项目中到处维护 URL 是否容易出错?

解决办法是在一个中心化的 deps.ts 中重新导出所依赖的外部库,它和 Node 的 package.json 具有相同的作用。

举个例子,您正在一个大型项目中使用一个断言库,您可以创建一个 deps.ts 文件来导出第三方代码,而不是到处导入 "https://deno.land/std/testing/asserts.ts"

export {
  assert,
  assertEquals,
  assertStrContains,
} from "https://deno.land/std/testing/asserts.ts";

在这个项目中,您可以从 deps.ts 导入,避免对相同的 URL 产生过多引用。

import { assertEquals, runTests, test } from "./deps.ts";

这种设计避免了由包管理软件、集中的代码存储库和多余的文件格式所产生的大量复杂性。

标准库运行错误

标准库中的一些模块使用了不稳定的 Deno API。

不用 --unstable 命令行选项运行这些模块会产生一些 TypeScript 错误,表示 Deno 命名空间中不存在一些 API:

// main.ts
import { copy } from "https://deno.land/std@0.50.0/fs/copy.ts";

copy("log.txt", "log-old.txt");
$ deno run --allow-read --allow-write main.ts
Compile file:///dev/deno/main.ts
Download https://deno.land/std@0.50.0/fs/copy.ts
Download https://deno.land/std@0.50.0/fs/ensure_dir.ts
Download https://deno.land/std@0.50.0/fs/_util.ts
error: TS2339 [ERROR]: Property 'utime' does not exist on type 'typeof Deno'.
    await Deno.utime(dest, statInfo.atime, statInfo.mtime);
               ~~~~~
    at https://deno.land/std@0.50.0/fs/copy.ts:90:16

TS2339 [ERROR]: Property 'utimeSync' does not exist on type 'typeof Deno'.
    Deno.utimeSync(dest, statInfo.atime, statInfo.mtime);
         ~~~~~~~~~
    at https://deno.land/std@0.50.0/fs/copy.ts:101:10

解决方法是加上 --unstable 选项:

$ deno run --allow-read --allow-write --unstable main.ts

要确定哪些 API 是不稳定的,请查阅类型声明 lib.deno.unstable.d.ts

这个问题会慢慢来解决,毕竟 Deno 还是太年轻啊。

URL 引入的包每次执行都要下载吗?

答:只需要再执行一次就能明白,不需要每次下载。

运行

> deno run index.js
Download https://deno.land/std/fmt/colors.ts
Compile https://deno.land/std/fmt/colors.ts
hello world!

我们看到其有 DownloadCompile 两个步骤。

当你第二次再运行的时候,就会发现并不会再有 DownloadCompile 两个步骤了:

> deno run index.js
hello world!

Download 和 Compile 的文件在哪里呢?

我们发现,当前执行的目录,并没有 DownloadCompile 文件,那文件放在哪里呢;

首先来看一下 deno --help 命令:

> deno --help
SUBCOMMANDS:

# ...

info           Show info about cache or info related to source file

# ...

ENVIRONMENT VARIABLES:
    DENO_DIR   Set deno's base directory (defaults to $HOME/.deno)

deno info 命令展示了依赖关系,类似 package.json。

> deno info index.js
local: /Users/Administrator/Desktop/index.js
type: JavaScript
deps:
file:///Users/Administrator/Desktop/index.js
  └── https://deno.land/std/fmt/colors.ts

DENO_DIR 则为实际的安装和编译目录,相当于 node_modules,默认为 $HOME/.deno

(命令提示是这样的,但实际需要指定一下环境变量 export DENO_DIR=$HOME/.deno),我们看一下:

> tree $HOME/.deno
/Users/Administrator/.deno
├── deps
│   └── https
│       └── deno.land
│           ├── 3574883d8acbaf00e28990ec8e83d71084c4c668c1dc7794be25208c60cfc935
│           └── 3574883d8acbaf00e28990ec8e83d71084c4c668c1dc7794be25208c60cfc935.metadata.json
└── gen
    └── https
        └── deno.land
            └── std
                └── fmt
                    ├── colors.ts.js
                    ├── colors.ts.js.map
                    └── colors.ts.meta

8 directories, 5 files

没网络了怎么办?

有些场景是将本地写好的代码部署到没有网络的服务器,那么当执行 deno run xxx 时,就是提示 error sending request。

应对方法:将上面的缓存目录内容,直接拷贝到服务器并指定环境变量到其目录即可。

依赖代码更新了怎么办?

当依赖模块更新时,我们可以通过 --reload 进行更新缓存

> deno run --reload index.js

我们还可以通过白名单的方式,只更新部分依赖。例如:

> deno run --reload=https://deno.land index.js

仅缓存依赖,不执行代码有办法吗?

解:有的,我们可以通过 deno cache index.js 进行依赖缓存。

多版本怎么处理?

解:暂时没有好的解决方案,只能通过 git tag 的方式区分版本。

标准模块 与 node API 兼容

我们通过第 1 点可以看到,其实 deno 的 API 相对于 node 其实是少一些的,通过其文件大小也能看出来:

> ll /usr/local/bin/node /Users/Administrator/.local/bin/deno
-rwxr-xr-x  1   42M   /Users/Administrator/.local/bin/deno
-rwxr-xr-x  1   70M   /usr/local/bin/node

那这些少的 API 只能自己写或者求助于社区吗?

deno 对于自身相对于 node 少的和社区中常用的功能,提供了标准模块,其特点是不依赖非标准模块的内容,达到社区内的模块引用最后都收敛于标准模块的效果。

例如:

// 类似 node 中 chalk 包
import { bgRed, white } from "https://deno.land/std/fmt/colors.ts";

// 类似 node 中的 uuid 包
import { v4 } from "https://deno.land/std/uuid/mod.ts";

同时为了对 node 用户友好,提供了 node API 的兼容

import * as path from "https://deno.land/std/node/path.ts";
import * as fs from "https://deno.land/std/node/fs.ts";

console.log(path.resolve('./', './test'))

所以,大家在为 deno 社区做贡献的时候,首先要看一下标准模块有没有提供类似的功能,如果已经提供了可以进行引用。

异步操作

根据 ry 自己是说法,在设计 node 是有人提议 Promise 处理回调,但是他没听,用他自己的话说就是愚蠢的拒绝了。

node 用回调的方式处理异步操作、deno 则选择用 Promise

// node 方式
const fs = require("fs");
fs.readFile("./data.txt", (err, data) => {
  if (err) throw err;
  console.log(data);
});

另外 deno 支持 top-level-await,所以以上读取文件的代码可以为:

// deno 方式
const data = await Deno.readFile("./data.txt");
console.log(data);

node 关于这方面也在一直改进,例如社区上很多 promisify 解决方案,通过包裹一层函数,实现目的。例如:

// node API promisify
const { promisify } = require("es6-promisify");
const fs = require("fs");

// 没有 top-level-await,只能包一层
async function main() {
  const readFile = promisify(fs.readFile);
  const data = await readFile("./data.txt");
  console.log(data);
}

main();

单文件分发

我们知道 npm 包必须有 package.json 文件,里面不仅需要指明 mainmodulebrowser 等字段来标明入口文件,还需要指明 name 、licensedescription 等字段来说明这个包。

ry 觉得这些字段扰乱了开发者的视听,所以在 deno 中,其模块不需要任何配置文件,直接是 import url 的形式。

去中心化仓库

对于 www.npmjs.com 我们肯定都不陌生,它是推动 node 蓬勃发展的重要支点。

但作者认为它是中心化仓库,违背了互联网去中心化原则。

所以 deno 并没有一个像 npmjs.com 的仓库,通过 import url 的方式将互联网任何一处的代码都可以引用。

PS:deno 其实是有个基于 GitHub 的第三方模块集合。

注意:Npm 也是支持任意合法 Git 仓库引用的。并不能因为 npmjs 官方的强大,就说它是中心的。

去开发依赖

我们在写一个 node 库或者工具时,开发依赖是少不了的,

例如 babel 做转化和打包、jest 做测试、prettier 做代码格式化、eslint 做代码格式校检、gulp 或者 webpack 做构建等等,

让我们在开发前就搞得筋疲力尽。

  • deno 通过内置了一些工具,解决上述问题。
  • deno bundle:打包命令,用来替换 babel、gulp 一类工具:例如:deno bundle ./mod.ts;
  • deno fmt:格式化命令,用来替换 prettier 一类工具,例如:deno fmt ./mod.ts;
  • deno test:运行测试代码,用来替换 jest 一类工具,例如 deno test ./test.ts;
  • deno lint:代码校检(暂未实现),用来替换 eslint 一类工具,例如:deno lint ./mod.ts。

AXIHE / 精选资源

浏览全部教程

面试题

学习网站

前端培训
自己甄别

前端书籍

关于朱安邦

我叫 朱安邦,阿西河的站长,在杭州。

以前是一名平面设计师,后来开始接接触前端开发,主要研究前端技术中的JS方向。

业余时间我喜欢分享和交流自己的技术,欢迎大家关注我的 Bilibili

关注我: Github / 知乎

于2021年离开前端领域,目前重心放在研究区块链上面了

我叫朱安邦,阿西河的站长

目前在杭州从事区块链周边的开发工作,机械专业,以前从事平面设计工作。

2014年底脱产在老家自学6个月的前端技术,自学期间几乎从未出过家门,最终找到了满意的前端工作。更多>

于2021年离开前端领域,目前从事区块链方面工作了