正文
Tips: deno 项目现在属于飞速发展的阶段,源码随时可能更新,所以这篇文章中的代码只是作为说明需要,和项目中的代码不保持同步。展示代码只保留核心,异常检查等会酌情删去。
最近前端风起云涌,
Ryan Dahl
大佬的
deno
甫一开源就引起了巨大的关注,甚至引发了一些不太好的刷 issue 事件。不过 deno 这一席华美的袍之下,到底隐藏着什么呢?这个系列文章就结合源码来对
deno 的做一点简单的分析与梳理,也算是一份学习笔记了。
deno 是什么?
这个问题非常好回答,在 deno 的
GitHub 主页
上有着非常明确的定义:
A secure TypeScript runtime on V8
,也就是
一个基于 v8 的 TypeScript 运行时
。单纯看定义,可能有些盆友会发出「每个字都能看懂但是合在一起就不懂」的感慨,不过我们可以采用类比法来理解这个定义。
在 Node.js 的
GitHub 主页
上,写着一段相似的定义:
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js 是一个基于 v8 的 JavaScript 运行时
。这么一对比就非常明显了, deno
和 node 的定位相似,都是一个「运行时」,而且底层都是基于知名的 JS 引擎 v8,不过一个支持 TS,一个支持 JS。举个最简单的例子,我们可以写出这样的代码:
// hello.ts
const name: string = "yingying";
console.log(name);
然后我们可以用 deno 的可执行文件来运行这个
hello.ts
代码文件
$ deno ./hello.ts
yingying
有个很有意思的点,Node 和 deno 在名字上只是简单地调换了一下字母顺序,这更是说明了两者的兄弟关系 :) 。
deno 有什么特点?
Node 之父选择再做一个定位与 Node 类似的运行时出来,这肯定是有很多理由的,正巧在 jsconf 2018 上,Ryan Dahl 专门阐述了他设计 deno 的原因,感兴趣的可以阅读
Design Mistakes in Node
这篇 PPT,我这里只是针对 deno 的主要特点进行简单的描述和解释。
安全
这里所说的安全不止是二进制代码安全,还包括比较容易被人忽视的 IO 安全。
在 Node 中我们可以利用
vm
来构建沙盒运行环境,然后调用 vm 上的相关方法来执行相应操作。而在 deno 中则是隔离地更为彻底,我们只能通过消息的发送与接收来进行系统调用,将恶意代码完全隔绝在实现层(go、c++)而不是调用层(ts)之中。
除此之外,在 deno 中运行的 ts 代码将默认没有网络请求和文件读取的权限,用户必须在运行代码的时候手动加上
--allow-net
和
--allow-write
等 flags 来开启相应的权限,这样可以更安全地运行第三方的代码。
简化模块系统
Ryan 认为 Node 的模块系统太过繁琐复杂,
package.json
文件更是包含了过多的冗余信息,所以在 deno 中,模块系统选择向 go 看齐,通过 url 或者相对路径来引用文件,并且需要指定文件的格式,而不再像 Node 中的利用
resolve 算法
来进行文件路径补全。通过网络加载的文件会被缓存,下次引入会直接加载缓存文件。而模块的版本控制则是通过在包名中加入语义化版本号来实现,比如
https://unpkg.com/[email protected]/testing.ts
, 去中心化的版本控制可以实现更细粒度的依赖管理。
面向 TypeScript
没有人不喜欢 TypeScript
在 8102 年,TypeScript 已经毫无争议地成为了(前端)新项目开发和老项目重构的 dream language,关于它的优点够再写一篇文章了,所以这里就按下不表。deno 中 主要是修改了一些 TS compiler 中的钩子函数,以支持新的模块查找策略。比如
fileExists
:
class TypeScriptHost implements ts.LanguageServiceHost {
// ts 转换器的钩子函数之一
fileExists(fileName: string): boolean {
// 这里的 resolveModule 就是自定义的模块解析函数
const m = resolveModule(fileName, ".");
const exists = m != null;
util.log("fileExist", fileName, exists);
return exists;
}
}
deno 的不足
deno 本身虽然说是「TS runtime」,但是由于基于的运行引擎是一个 JS 的引擎(V8),所以需要在 runtime 保持一个 ts compiler,来自动地将 ts 转译为 js ,最后交给 v8 来执行,这无疑增加了启动性能与运行的性能,关于这一点的讨论可以参见
Compare bootstrap speed with Node.js
。