1. 程式人生 > 實用技巧 >為什麼Deno將會取代Node.js

為什麼Deno將會取代Node.js

筆者一直從事A.I. 於人工智慧相關工作。專注於如何運用大資料及人工智慧在電商,金融,政府的相關服務。其中也有參與過十幾個落地於廣州,橫琴,香港,澳門等大型專案上,擁有相當豐富的經驗。

最近也成為了一名支援中國開源社群中的一名開發者,也希望各位多多支援。TrackSoul是用於事件跟蹤的開源專案,跨平臺解決方案。 它支援網站,Web APP,IOS,Android甚至微信上的小程式。改善使用者體驗的唯一穩定方法是依靠使用者行為資料。 無論是決定優化使用者介面,營銷,銷售量,還是預測性分析。 收集資料並不難,要最大程度地提高資料使用效率是我們要關注的重點,我們希望提供從收集到分析的整體解決方案。 我們希望為該專案構建一個長期可維護的生態系統,以擴充套件使用者行為的自由度。這個專案不單止提供收集資料的 Javascript的工具包,更是提供一個完整使用者行為分析平臺 !

概覽(前言):

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/[email protected]/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 = 3000

app.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/[email protected]/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版本吧。