Deno 1.0 来了 它会取代 Node 吗?

最近,在经过了漫长的开发周期后,Deno 1.0 终于面向开发者正式推出了,对于热衷于使用 JavaScript/TypeScript 进行开发者的开发者来说,Deno 是一个非常值得关注的项目。
有不少开发者认为 Deno 在未来非常有发展前景,随着时间的推移,TypeScript 的应用可能会变得越来越广泛,而 Deno 在功能趋于完善、稳定之后也会拥有一批的追随者,会有越来越多来自社区的力量参与到 Deno 生态的建设当中。

从长远来看,虽然现在 Node 已经在前端成为了一个支柱一般的存在,几乎所有现代化的前端开发都已经离不开 Node。它确实给前端工程化带来了突飞猛进的进步,没有 Node,就没有 Webpack、没有 Gulp、没有 Grunt 这些优秀的、能够让前端实现高度工程化的工具,前端技术的发展肯定也没有现在这般迅速。

但是,热门和最好、最优并不是划等号的,随着前端开发逐渐往工程化的方向走,很多项目在这种概念下越做越大,Node 本身在设计上、架构上的一些问题也开始暴露了出来,比较熟悉 Node 的朋友们应该都对此有一定的感受,Node 确实在设计上存在一些很不优雅、很让人头疼的地方。
很多问题在 Node 其实是一个很简单的问题,但是出于 Node 的设计,这一个问题被大大地复杂化,给开发者带来了很多不必要的东西,也给开发带来了更大的复杂性和难度。

虽然 Node 一直以来也在自我完善、自我进化,社区也在不断进步探索新的方案,但是有的问题并不是通过这种自我完善、自我进化可以解决的,毕竟它和 Node 本身的设计有关,如果要彻底解决这方面的问题,那么 Node 可能要推倒重来了。

即使这个新版本它还叫 Node,但是它会不可避免的和旧版本产生一些割裂。考虑到 Node 在目前已经被非常广泛的使用,它也不可避免背上了一些历史包袱,在这种情况下,Node 其实很难再去做这种会造成严重割裂的、可能会给现有 Node 生态带来很大影响的改变。
这个时候,Deno 作为一个新框架,它没有历史包袱,在 Deno 发展的过程中,他们也决定不完全兼容现有的 Node 生态,这给了它相较于 Node 的一个巨大优势 —— 它可以从设计上根本性地解决 Node 现有的问题,给开发者带来一个更加优雅、更加便利的开发体验。
长远来看,如果 Deno 未来的走势非常好,它有可能会取代 Node,成为 JavaScript/TypeScript 开发的最热门框架。

很有意思的是,Deno 的作者其实和 Node 是同一位,它也是由 Ryan Dahl 创建的项目。由于作者本人就是当年设计 Node 的人,所以他自然也很清楚 Node 在设计上存在的缺陷,Deno 最开始本身就是作为 Node V2 来设计的,从这个角度来说,其实 Deno 本身就已经肩负了接班 Node 的使命。

也正是因为这一点,对于使用 JavaScript/TypeScript 的开发者来说,Deno 是一个很重要的存在,它的发展很有可能会影响到未来前端开发,甚至是后端开发。
在开发语言的选用上,Deno 选择了比较前沿的 Rust,而不再像 Node 那样使用 C++,但是和 Node 一样,Deno 本身是并没有脱离 V8 的,它仍然需要 V8 来执行 JavaScript,只是整个框架的编写采用了全新的语言、全新的架构。

相较于 Node,Deno 有一个巨大优势 —— 它先天支持了 TypeScript。很多朋友在日常开发中可能还用不到 TypeScript,但是在行业里,特别是很多大型项目,TypeScript 实际上用得还是非常普遍,因为它在 JavaScript 引入了类型,并且可以配合工具实现代码的静态类型检查,这能够规避 JavaScript 作为一个弱类型语言在大型项目开发中带来的很多问题。

尤其是在后端开发中,当一个项目变得越来越大后,弱类型 + 缺少足够的说明和注释很可能造成代码里出现一些类型引起的坑,因为 JavaScript 的机制中存在不少默认的隐式类型转换,而这些转换有时候会让程序跑出意外的结果。一旦出现这样的错误,在大型项目中,想要排查出这类错误是一件相当有难度的事情。

与其靠程序员们的自觉和各种注释来规避类型可能带来的问题,不如引入一套类型机制,直接从根本上解决 JavaScript 作为一个弱类型语言在大型项目开发,尤其是在后端开发中可能带来的问题。
类型机制和工程化是互相匹配、相辅相成的,目前有很多开发者已经迁移到了 TypeScript,使用 TypeScript 进行开发。但是由于 Node 本身只支持 JavaScript,以至于 TypeScript 只能够借助 Babel 之类的工具,经过一层编译才能够运行。
不论是开发、调式还是最后的打包部署,这种情况都会不可避免的给开发者带来更多的麻烦,而 Deno 作为一个新兴的框架,它具备了直接运行 TypeScript 的能力,这意味着使用 Deno 的开发者将不再需要像 Babel 这样的工具来处理 TypeScript,这能够大幅简化项目的配置,同时能够从项目中剥离掉不少的依赖。
说到依赖,Node 目前最让开发者头疼的,反倒不是 TypeScript 等问题,而是体积庞大、动不动就有数以十万、百万计小文件构成的 node_modules,它是目前 Node 发展过程中最需要解决的一个问题。

在笔者看来,即使是经过了一些优化,node_modules 仍然是一个相当糟糕的存在,每一个项目都会有一个 node_modules,其中可能包含了非常非常多重复的文件,项目稍微一多,开发者的电脑里就会被塞入几百万个小文件,相当相当不优雅。
如果开发者使用的还是机械硬盘,这个事情就更加糟糕了,机械硬盘在处理这类文件时性能是非常低的。而且对于 Node 项目,项目的依赖可能还会依赖其他的东西,而这个依赖可能又依赖其他的包,哪怕你只是安装一个 react 之类的项目,你的 node_modules 就会瞬间被塞下大量的文件,这种「依赖地狱」会使得开发者在管理项目依赖的时候变得十分困难。

而且还有一个很重要的事情是,很多依赖本身都是开源的,一个项目它本身可能很简单,但是因为它所用的框架依赖非常大,而导致项目堆满了各种各样的依赖。这些依赖的代码都是难以审查的,没有人能够保证他们 100% 可用、100% 好用,任何的依赖都有可能出现坑,即使现在不存在坑,你也不能保证它未来不出现坑,或者你在升级它之后,它能和项目内用到它的其他依赖是契合的。
所幸,在 npm 的努力下,至少依赖地狱和依赖版本控制的问题在当下得到了一个比较好的解决,就目前来说,node_modules 的核心问题还是在于它过于臃肿、难以管理,特别是在你想要重装所有依赖的时候。
当然,现阶段 npm 越来越复杂的逻辑也会给开发带来一些额外的问题,而且 npm 本身也存在一些抢注包名等的难以解决问题,对于国内的开发者来说,npm 的下载速度也是很堪忧的,而且开发者很可能要一次性下载相当大量的包。

为了避开 node_modules,Deno 在设计上直接取消了 node_modules,采用了一种完全不一样的依赖加载方式。
你可以从任何的位置加载你想用的依赖包,这个位置可以是本地,也可以是 Web。也就是说,你可以直接从 GitHub,或者是某个托管了大量依赖的服务器上直接加载这些依赖。虽然说这些代码还是会被下载下来存储到你的磁盘上,但是它本身对你来说是完全隐藏的,由 Deno 进行统一的管理。
你不再需要给每个项目配置 package.json 并且下载一个臃肿的 node_modules,不再需要看着项目里 node_modules 装着的那一堆文件夹和小文件发愁,Deno 会帮你管理好这些文件,解决这一系列的问题。
这个设计其实相当巧妙,它直接解决了版本控制方面的问题,同时也让依赖变成了非中心化的形式,开发者在使用依赖的时候可以更加灵活、更加自由。
在模块分发的时候,Deno 也不再要求需要发布一整个项目,不要求开发者去配置 package.json 中关于项目的各种属性,以及项目许可证之类的内容,也不需要开发者再通过某种描述去规定入口文件等等。你只需要把你认为的那个模块入口文件分发出去即可,其他人只需要用 import URL 的形式导入,就能够载入依赖。
相较于现在 Node 这一套模块分发的模式,Deno 这边是切实做到了化繁为简。

在 node_modules 这个问题上,之所以它里面会包含很多杂乱无章的模块,还有一大原因是因为很多项目使用的一些基础的东西,它可能本身是一个依赖、是一个外部的模块,而不是这个项目内部的代码,比如说 core-js。

有的时候,哪怕模块本身实现的是一个很简单的功能,代码只有很简单的几行,它也可以作为一个模块分发出去,被很多开发者使用,因为很多开发者可能会认为依赖做到了一个更好的实现,或者是单纯地因为懒,不像再写一遍这个东西,而使用依赖。
由于 npm 上模块的发布是很随意的,任何开发这都可以发布自己的包,这使得很多内容很简单、功能很基础的包也有很多种不同的版本,这就造成了一个情况:

A 依赖使用的是甲包,B 依赖使用的是乙包,C 依赖使用的是丙包,而甲、乙、丙三个包实际上实现的是同一个东西。但是出于 Node 和 npm 的机制,在你的 node_modules 内,你必须要下载着三个包,当然,这还是最好情况,假如有更多依赖用这样的基础功能包,但用的版本不一致,你可能还要把同一个包的不同版本也都下载下来。

这无形中大幅让 node_modules 变得更加臃肿,为了解决这个问题,Deno 官方选择面向开发者维护一个「标准库」,所有这些很基础、通用的东西都会由标准库中的内容实现,这样一来开发者也就不需要去花时间寻找各种各样实现了某些基础功能的模块,同时依赖的复杂程度其实也会有一定程度的降低,毕竟在标准库这一套模式下,大家不会再去选用各种各样本质上实现了同样内容的第三方库。
Deno 的期望是让所有第三方库最终都会收敛于标准库,让标准库成为生态里真正的标准。

在简化这方面,为了简化代码的编译、打包等一系列的内容,Deno 也原生支持了对代码的打包、测试、格式化,如果 Deno 原生提供的这一套工具能够让你感到满意,那么你的开发会比在 Node 上简化非常非常多,至少你可以节约掉一大批工具的安装和配置。
说到库,为了防止 Deno 这种依赖模式中可能会出现的一些恶意代码侵入,Deno 本身也设立了一套安全机制,通过控制代码的权限来保证一定程度的安全,这种机制也是 Node 完全不具备的。

在笔者看来,目前的 Deno 是一个前瞻性非常足的框架,它的很多设计都直接地针对了 Node 现有的问题,在 Node 的基础上做「减法」,把 Node 本身一些很复杂的东西变得简单化,去给开发者带来一个更好的体验。
光就这一点来说,Deno 就足够成为一个值得关注、值得尝试的框架。除此之外,Deno 还有一个巨大的优势在于,它面向开发者提供了开箱即用的 TypeScript 支持,随着 TypeScript 热度的不断走高,Deno 或许会有一个相当不错的发展前景。
至少在目前,笔者是比较看好 Deno 这个框架的,在未来笔者也会对 Deno 保持一个高度的关注,只要它的生态能够稳步发展,逐渐走向成熟,未来 Deno 取代掉 Node,其实也不是没有可能的事情。即使取代不了,它也很有可能会达到一个近似于现在 Node 的高度,和 Node 并存。