Node.js終結者?青出於藍的Deno(一)
2020-08-06
有編程經驗的人,都一定會聽聞過Node.js
,Node.js
基于Chrome
的V8
引擎開發,本身能夠運行JavaScript
,在前端開發(Frontend Development)、後端開發(Backend Development)、Android及iOS開發(Android & iOS Development),都有Node.js
的蹤影,更帶起全JS開發
的潮流,也就是大家常常在Youtube
上看到的MEAN Stack
(Mongodb
,Express
,Angular
,Node.js
),也是以Node.js
為中心發展起來的。
然而,Node.js的作者Ryan Dahl
卻在2018年六月JsConf EU
之上,發表了一個題目為10個Node.js的設計錯誤的短講。出乎意料的是: 原來Ryan
2012年後已離開Node.js
開發工作,原因是他的興趣主要在伺服器編程(Server Programming)之上,所以轉投了Golang
的懷抱,所以已一段長時間沒有用Node.js
作開發工作。而更重要的是,他2012年離開時,認為Node.js
作為一個後端運行JavaScript
程式,已是大功告成。他萬萬沒有想過Node.js
會變成今天般無所不在(ubiquitous),因此很多設計上沒有周詳考慮,現在已經是Too late to change, Too big to fail
。
螢幕截圖自影片10 things I regret about Node.js - Ryan Dahl
綜觀全片,有趣的是Ryan Dahl
其實只講了七個問題。提出問題,而不提出答案沒有多大意義,Ryan
亦提出了他心目中的解答,也就是本文要介紹的deno。
Deno
是一個能夠原生運行JavaScript
及TypeScript
的程式庫,名字由來是將Node
的字母重新排列而得來(no-de
到de-no
),標誌是一隻可愛的恐龍,原因也許是因為deno
跟恐龍英文dinosaur
聽起來很像?筆者也不太清楚。
Ryan
在2018年時就開始了Deno
的開發,事隔兩年,在2020年5月,正好推出了1.0,標誌著Deno
技術開始成熟,可以挑戰Node.js
了!
現在我們就在以下七個方面,將Node.js
及deno
大比拼一番!
Node.js官方API 使用Callback VS Deno原生使用Promise
Node.js
開發者都知道著名的Callback Hell
(回調地獄),因為Node.js
的官方API全是以Callback寫成,即使是讀取檔案如此簡單之事,都需要用到Callback Function(回調函數)。
const fs = require('fs'); fs.readFile('./my-file.txt',function(err,data){ //<-- 這個就是回調函數 //數據在回調函數之內才能使用 if(err){ console.log(err); return; } console.log(data.toString()); })
相反,如果使用新的Promise
API,程式碼就會大大簡化:
const fs = require('fs'); async function main(){ const data = await fs.readFile('./my-file.txt'); console.log(data.toString()); }
Node.js
開發初期早就支援Promise
,但在2010
年作者覺得不太有用,又移走了Promise
,結果兜兜轉轉到了版本0.11
才重新加入Promise
,浪費了不少開發者的青春在回調地獄之上..... 而由於一開始的官方API 就是使用Callback
,因此為了向下兼容(Backward Compatibility),因此即使到了版本14,官方API依然是Callback
為主,Promise
為輔,變相任何初學者都要同時學習Callback
及Promise
,變相學習量加倍。
Deno
呢?當然不會重蹈覆轍,一開始就完整支援Promise
與Async/Await
。
const fileContent = await Deno.readTextFile('./my-file.txt') console.log(fileContent);
Deno
不僅支援Promise
及Async/Await
,也完整支援TypeScript
,連帶Top Level await
這樣的新功能也一併支援了。
簡潔程度直迫其他Dynamic Languages
(動態語言)啊。
要將字串寫入檔案也很簡單。也是一句完成。
await Deno.writeTextFile('./my-file.txt','Hello World!',{append:true});
Node.js無掩雞籠 VS Deno安全沙盒
非香港讀者注:無掩雞籠乃粵語俗語,意指自出自入,無任何保安可言
Node.js
的安全性(Security)很有問題嗎?其實當你運行一個Node.js
檔案時,如以下這句command
,本身已是危機四伏。
node index.js
index.js
擁有你現有使用者(Current User)的所有權限(All Permissions)。假如這個js
檔是由黑客所寫之病毒檔案,每當你運行時,就會將你所有個人檔案加密(Encryption),然後再向你勒索bitcoin
才會解密(典型CryptoLocker旳攻擊方法)。除非你逐行細心閱讀該檔案內容,否則這樣的攻擊是無法避免的。此問題不只是Node.js
專有,不論是Python
、PHP
、C#
、Java
,這個問題一樣存在。所以,傳統程式語言其實是「無掩雞籠」的!
Deno
則不然,由於Node.js
及Deno
都是基於V8引擎所開發的,而V8又是Google Chrome
的核心套件,瀏覽器的安全性要求非常高,這樣的安全問題,在瀏覽器世界其實早已解決,只是作者在之前編寫Node.js
時,沒有加入這些防禦措施。
上面readFile
的例子,如果你用deno
直接運行,寫法是deno run read-file.ts
。
$ deno run read-file.ts Check file:///your/path/to/deno-test.ts error: Uncaught PermissionDenied: read access to "./my-file.txt", run again with the --allow-read flag at unwrapResponse (rt/10_dispatch_json.js:25:13) at sendAsync (rt/10_dispatch_json.js:76:12) at async open (rt/30_files.js:52:17) at async Object.readTextFile (rt/40_read_file.js:30:18) at async file:///your/path/to/deno-test.ts:5:21
出現了PermissionDenied
錯誤,因為deno
預設是沒有讀取檔案的權限。因此deno run read-file.ts
無法讀取檔案。
加上-allow-read
的選項,才是正確用法:
deno run --allow-read read-file.ts
同理,要寫入檔案,我們就需要選項--allow-write
,否則就會得到以下錯誤。
error: Uncaught PermissionDenied: write access to "./my-file.txt", run again with the --allow-write flag
為何要開發者親自輸入呢?因為Deno
建立了一個沙盒(Sandbox),將程式在其中運行:沙盒內之程式碼除非獲得授權,否則無法與電腦其他任何資料、任何硬件互動。因此保證了運行deno run deno-test.ts
這個動作百分百分安全。Deno
除了--allow-read
及--allow-write
控制檔案讀寫之外,還有其他不同權限控制: 包括--allow-net
控制網絡存取、--allow-run
控制子進程存取等等。以沙盒保證程式運行的安全性,在芸芸程式語言中是首創,將運行程式的安全性放在第一位。
Node.js組建系統GYP VS Deno FFI(開發中)
如果大家安裝過一些Node.js
的著名套件,例如Tensorflow
、Bcrypt
的話。都一定會見過以下這個惱人的錯誤。
GYP
全名是Generate Your Project
,用作生成其他組建系統的元組建系統(Meta-build system for build system
),最主要用途在生成一些不是原生JavaScript
的程式庫時用到,由於Tensorflow
、Bcrypt
都會用到native code
,因此這個煩人的問題,就代表你編譯native code
時出現問題!又不知要耗費多少除錯的時間了...
那為何Node.js
最終會使用了GYP
呢?其實是由於原本V8
引擎一開始是使用GYP
的,所以Node.js
亦仿隨。但後來V8
用了另一個元組建系統GN
,Node.js
就陰差陽錯下成了唯一使用GYP
的主流程式庫。而GYP
的格式很古怪,你說是JSON
,但其實又有Python
的語法,就像兩者的合體。
{ 'target_name': 'hello', 'sources': [ 'kitty.cc', ], 'include_dirs': [ 'shared_stuff/public', # Merged, list item prepended due to include_dirs+ 'headers', ], 'link_settings': { 'libraries': [ '-lm', '-lshared_stuff', # Merged, list item appended ], 'library_dirs': [ '/usr/lib', ], }, 'test': 1, # Merged, int value replaced }
GYP
這個怪胎,就導致了許多編譯(Compilation)上的問題,燃燒了多少的青春...
筆者在開發時,也會盡量避免需要gyp
的套件,因為通常問題多多。
Deno
則採取更「正常」的做法,也就是使用FFI(Foreign Function interface)
,方便編程者直接將其他語言的程式與Deno
一齊使用。不過這個功能正在開發之中,所以大家請拭目以待。
稍息一下
篇幅所限,這次就先理解這三點,下篇我們會繼續漫談Deno
這個後起之秀!
留言
閱讀更多
到底React Hooks有何特別(二)?淺談useEffect及useReducer
2018-11-29
於本篇文章的上集,我們討論了useState如何令Stateful React Component簡化良多,此篇主要討論的是如何使 用useEffect。useEffect可以簡化state,很多人都提到React Hooks有可能可以完全取代Redux作為 React State Management的標準,正因如此。
Web Technology為何征服世界?
2019-02-05
2007年,蘋果宣佈發佈第一代iPhone,標誌智能電話時代的開始;一年之後Android亦宣告面世,從此時起,智能電話的發展迅速,Mobile App成為軟件的代 名詞,筆者初初成為軟件工程師時,總有朋友詢問我是否正在開發Mobile App,縱使筆者的專業一直都是網頁及後端開發之上。而其時亦有不少預測,預測[網站將會被Mobile App完全取代](https://searchenginewatch.com/sew/opinion/2414336/the-final-hurdle-is-cleared-apps-will-replace-websites)。網站所用的HTML、CSS、JS等,亦將成為歷史,送入博物館之內。
Dart vs JavaScript vs TypeScript
2019-02-17
隨著Flutter受到開發者的重視,Google於2011年推出的Dart又重新進入大家關注的視野之內,不過除了Flutter以外,其實Google的開發者早在2016年也推出過Angular Dart,讓開發者以Dart開發網站應用,不過由於Angular Dart對比TypeScript版Angular文本長期不足,因此沒有引起太多關注。Google推出Flutter,可以說為大家對Dart的信心注入了一劑强心針,大家又重新開始關注這個已有8年歷史的程式語言。 本文想介紹的是,就是到底Dart有何特色?與JavaScript比較,又有何優劣?由於TypeScript開始於前端日漸盛行,我們亦可以趁機比較一下三種語言的異同。
SQL首部曲:NoSQL? No! SQL!
2019-10-08
由本篇開始,接連四篇都是與SQL有關的文章,會想寫SQL的原因,是因為SQL在現今軟件開發及數據科學佔有舉足輕重之地位,卻總是在背後默默無名,從未見得到像其他新興技術之關注,有見及此,筆者決定介紹SQL之特點,順便破除一些對使用SQL上常有的誤解。
Node.js終結者?青出於藍的Deno(二)
2020-08-21
返回文章的題目,Deno能否取代Node.js成為JavaScript的主要開發環境呢?筆者認為短期兩至三年內可能性都非常低,但如果未來Deno能夠解決筆者所言的兩個局限性,筆者也會躍躍欲試,將手頭上的Node.js專案加入Deno了。