为什么Deno将会取代Node.js

概览(前言):

Node.js(以下简称为Node)一直都深受JavaScript编程员社区欢迎,甚至拥有所有语言都无法扩展庞大的NPM套件管理工具社区。Node是由Ryan Dahl创建的,在Node还没面世之前,你和我可能也没有想过使用javascript同时间开发服务端和客户端,这意味着开发人员在各层上只需使用一种语言。也就是说,一个Web开发人员从其他前端技术转换到Node是很简单的。

然而在2018中,Node开发之父在JSconf公开承认了当初十个在设计和更新Node时犯下的错误,當中安全性、内存泄漏以及回調地狱等问题导致很多开发人员不敢在一些大型项目中使用Node來開發。直至今天,各大社区每天都有很多节点衍生出来的问题,其中包括“ Node内部的async-await不是真的同步”,“ npm仓库上存在大量质量参差不齐,年久失修的库导致安全隐患的问题”,“ 在使用npm install后,发现应用下载了一個几百mb的节点模块”等。当然最有效的方法是从节点的替代设计去优化,但是到今天,已经没办法去作任何重大的扩张及优化了,因为已经有太多Javascript的项目是基于Node了,包括相当庞大的NPM套件库。那为什么我们不去支持和尝试选择一个更好的方案呢?

Deno同样是由Ryan Dahl牵头开发的Javascript执行环境,而Deno 1.0终于在2020年5月13号正式面世,我本人也等了一年多了。Deno这个名字就是来自Node的字母重新组合,表示“清除Node.js“(de = destroy清除,no = Node)。虽然它正在处于一个早期开发阶段,但如果你现在是一个Javascript的编程人员或者是未来有意投身于网站开发相关的行业,那今天就是一个最好的时开始开始认识未来有最有潜力取代Node的Deno。它可以有效地从重叠代码的设计解决Node“天生缺陷”的各种问题。我想先说清楚,笔者并不认为未来一年甚至是三年,Deno能够完全取代Node现有的一部分,因为Node的生态环境已趨成熟以及Deno仍處於1.0的开发階段,但是未来我相信优胜劣势在软件开发当中是必然的。現在筆者將會帶領你一起探索Node的问题及Deno的解决方法。

Node Modules的问题:

1. Modules下载黑洞


各位应该也遇过跟笔者相同的情况,npm install 后,等了很久,然后发现npm竟然下载了几百mb 的node modules, 这是因为Node 里「Vendored-by-default」的相关机制导致的,当你下载一个node_module的时候,它会自动把全部的dependencies(依赖) 的modules也一拼下载,笔者以前也有试过项目刚开始没多久,开发人员push的代码数量只有几千多行,但node_modules内的代码数量已经达到几十万行。这个设计令要处理掉 node_module 的方式变得超复杂。 Ryan Dahl也曾经公开表示「It's my fault and I'm very sorry. Unfortunately it's impossible to undo now.」(这是我的错-十分抱歉。不幸的是,现在已经无法重做了),Node modules 的困扰,甚至有外国网友画了以下的图来表示Node modules 比黑洞更要深来讽刺比如。

在另一方面,Deno 完全舍弃了之前Node modules的CommonJS引入的做法,而選擇使用ES modules。相信做过前端开发的读者应该已经很熟悉了,在使用ES modules時你不再需要透过npm安装任何modules,使用者只需要从web伺服器内的url import进来,当你希望使用类似http/express 的框架时,你可以直接使用import url 像下面这样

import { serve } from "https://deno.land/std@0.53.0/http/server.ts";

2. 引入module


大家可能比较熟悉,当我们npm install之后,在项目中引入module的时候,其实并不需要输入 .js 的副档名,直接 const http = require(‘http’) 即可。實際上在浏览器中<script> 内的tag 是不能省略「.js」副档名的,这令Node要多执行几次沒必要的档案系统查询,才能猜到我们到底想要import 什么档案,导致运行速度变慢的情况发生。

3. 标准库

Node大多數的Module都是基於第三方的套件的,甚至是一些很常用的套件,因為在NPM內,相同功能的套件相當多,應該大多數的讀者也遇過類似的問題,因為Node官方API/包並沒有很完善。而Deno在标准库上很有特点,对常用功能也通通提供了官方的版本,保证可用性与持续的稳定性。以下也列出了一些与 npm 三方库的对比

這說明了,Deno 不單只是一個运行环境,它将会是一個完整基础生态,缓解了 Npm 生态下各種质量参差不齐的問題,大大提高了底层库的稳定性。但當然,Deno 生态也有三方库,而且本质上三方库和官方库在功能上没有任何壁垒,因为实现代码都类似。

安全性

软件/网站平台的安全性是其中一个最重要的因素来判定这个项目到底能不能上线以及用户的信息到底安不安全。我们来看一下如果我们使用Node来创建http服务器会是怎样.

首先我们需要创建一个index.js, 然后使用npm install安装所需的Node_modules (如 http / Express), 代码如下

const http = require('http');const server = http.createServer((req,res) =>{
    res.end('OpenSourceChina by Charles Lo');
});const port = 3000;
server.listen(port);console.log("HTTP web built with the port " + port);

相信各位可能已经很熟悉相关的使用方法,甚至有很多使用Node的人习惯了使用Express应用框架,代码如下

const express = require('express')const app = express()const port = 3000app.get('/', (req, res) => res.send('OpenSourceChina by Charles Lo'))
app.listen(port, () => console.log(`HTTP web built at http://localhost:${port}`))

其实两者都是非常相似的,我们一直都很喜欢那么简单,直观的操作。那我们现在看一下如果我們使用Deno來開發会是怎样呢?

我们可以创建一个index.ts , 对了,这里你已经能够看到跟Node有一点不太一样的地方,因为deno除了支持Javascript 以外,它也能够同时间支持 Typescript 的。

我们可以尝试一下在index.ts 内创建一个http server

import { serve } from "https://deno.land/std@0.53.0/http/server.ts";  const s = serve({ port: 3000 });  console.log("HTTP web built at http://localhost:3000/");  for await (const req of s) {

    req.respond({ body: "IBM developerworks by Charles Lo\n" });

 }

現在我们可以尝试运行一下相关代码,其实在deno执行的时侯也能够看到Node的一些风格

deno run index.ts

大家可以看到当Deno编译时,它会自动下载Import URL相关的档案,然后储存在本地的cache(缓冲存储器)内,因此在執行过之后并不需要再次下载相关的档案。简单来说这便是能够解决所有npm的弊病,就好像npm会自动安装一个庞大的modules dependencies(依赖) 资料夹的一个完整替代方案。

error: Uncaught PermissionDenied: network access to "0.0.0.0:3000", run again with the --allow-net flag

大家也可以看得到,刚刚Deno并没有正常地执行了,一个「安全性」的相关错误出现了。因为Deno是一个「安全」的运行环境,它不像Node, 当你npm安装套件的时候,你并不知道套件内的脚本安不安全,所有脚本都可以直接创建一个web server 又或者私自读写你本地任何档案和文件,脚本可以对你的电脑/服务器执行任何的动作。然而,Deno并不允许脚本在没有经过允许的情况下私底下创建一个web server 或者读取你的档案。

现在我们可以尝试一下使用—allow-net 允许Deno去创建网站服务器。

我们终于成功创建了并进入Web page了。当然 ,—allow-net 只是允许了deno去创建网站服务,所有任何对系统内的档案进行读写还是保持被禁止的状态。以下为deno其中的安全控制相关的参数

* --allow-read:打开读权限,可以指定可读的目录,比如--allow-read=/desktop/ibm* --allow-write:打开写权限* --allow-net=developer.ibm.com:允许网络通信,可以指定可请求的域,比如--allow-net=developer.ibm.com* --allow-env:允许读取环境变量

大家应该也能感受到Deno和Node之间安全性的区别了吧

构建系统:

大家可以看到上面的时间表,Chrome V8 是在2008件面世的,当时V8团队针对自己的项目特点,开发了一个叫做GYP(Generate Your Projects)的构建系统,Node在2009年面世的时候,Node用到的JavaScript Engine V8 也理所当然地使用了GYP来建构,但后来V8 转用了GN (Generate Ninja),這代表了只剩下Node 成为唯一的用户了。而在數據上顯示,使用GN的构建系统比起使用Python 写的GYP 快近20多倍,对于使用者来说,简直是天渊之别。 Ryan也曾经公开说过「The continued usage of GYP is the probably largest failure of Node core.」(继续使用 GYP 该是Node 核心的最大失误)。

异步问题:

其实早在2009年的六月份, Node 已经开始引入了JavaScript 的 Promises,但又在 2010年二月就移除掉了(笔者也不太理解Ryan当初的想法)。因此不同時期的使用者為了實現類似的功能時,都自行開發了非官方版本的Promise功能。這导致了Node在今天,仍然遍布着 async/await 和 promise 的不同 async API设计,大大提高了维护成本。现在Node.js API还是基于callback回调函数 , 并没有办法优雅地利用promises和 async/await。

大家刚刚上面看到使用deno创建web server的时候,可能也注意到以下的代码

for await (const req of s) {    req.respond({ body: "IBM developerworks by Charles Lo\n" });

  }

其实Deno在设计的时候,已经强调必须支持top-level await的了,比如说当我们在Node内读取文件的时候需要 -

const fs = require("fs");

fs.readFile(“./ibm.txt”, (err, data) => {  if (err) throw err;  console.log(data);

});

我们还是需要使用回调的方式处理异步操作。但在deno 则直接选择用 Promise -

const data = await Deno.readFile(“./ibm.txt”);
console.log(data);

虽然强大的Node生态环境一直都在改善Node衍生出来的各种问题,比如说使用promisify如下

const { promisify } = require("es6-promisify");const fs = require("fs");async function main() {    const readFile = promisify(fs.readFile);    const data = await readFile(“./oschina.txt);    console.log(data);
}

main();

但到最后,还是没有 top-level-await,只能包一层的操作

结束语

其实Deno还有很多值得大家探索的地方,Deno v1.0 开发团队也承诺「As of Deno 1.0.0, the Deno namespace APIs are stable. That means we will strive to make code working under 1.0.0 continue to work in future versions.」,这说明了Deno 1.0.0 的API已经是稳定了,随着未来Deno的叠代更新,大部分在今天能够使用的API在未来的Deno版本当中也是可用的。整体来说,Deno能够有效解决了你在写Node遇过的种种问题,是非常值得大家去探索研究的,我也希望在不久的将来,能够繼續为大家分享更多不同的领域和一起期待着下一个Deno版本吧。


评论 (0)

发表评论