這篇文章就是 Deno 的一個初步介紹,嘗試回答為什麼 Node.js 不能滿足需要,以及 Deno 能夠帶給我們什麼?
以下內容主要基於 Bert Belder 和 Ryan Dahl 的最新演講。
0、
進入主題之前,先說一下 Deno 這個詞怎麼發音。
兩種發音,"德諾"和"蒂諾",我都聽到過。看起來,"蒂諾"這個發音應該更合適一些,因為 Deno 的標誌是一隻恐龍。恐龍(dinosaur)的英文縮寫正是 dino。
1、
Deno 是 Ryan Dahl 在2017年創立的。
Ryan Dahl 也是 Node.js 的創始人,從2007年一直到2012年,他後來把 Node.js 移交給了其他開發者,不再過問了,轉而研究人工智慧。
他始終不是很喜歡 Python 語言,久而久之,就想搞一個 JavaScript 語言的人工智慧開發框架。等到他再回過頭撿起 Node.js,發現這個專案已經背離了他的初衷,有一些無法忽視的問題。
2、
首先,過去五六年,JavaScript 語言脫胎換骨,ES6 標準引入了大量新的語法特性。其中,影響最大的語法有兩個:Promise 介面(以及 async 函式)和 ES 模組。
Node.js 對這兩個新語法的支援,都不理想。由於歷史原因,Node.js 必須支援回撥函式(callback),導致非同步介面會有 Promise 和回撥函式兩種寫法;同時,Node.js 自己的模組格式 CommonJS 與 ES 模組不相容,導致遲遲無法完全支援 ES 模組。
其次,Node.js 的模組管理工具 npm,邏輯越來越複雜;模組安裝目錄 npm_modules 極其龐雜,難以管理。Node.js 也幾乎沒有安全措施,使用者只要下載了外部模組,就只好聽任別人的程式碼在本地執行,進行各種讀寫操作。
再次,Node.js 的功能也不完整,導致外部工具層出不窮,讓開發者疲勞不堪:webpack,babel,typescript、eslint、prettier......
3、
由於上面這些原因,Ryan Dahl 決定放棄 Node.js,從頭寫一個替代品,徹底解決這些問題。deno 這個名字就是來自 Node 的字母重新組合(Node = no + de),表示"拆除 Node.js"(de = destroy, no = Node.js)。
跟 Node.js 一樣,Deno 也是一個伺服器執行時,但是支援多種語言,可以直接執行 JavaScript、TypeScript 和 WebAssembly 程式。
它內建了 V8 引擎,用來解釋 JavaScript。同時,也內建了 tsc 引擎,解釋 TypeScript。它使用 Rust 語言開發,由於 Rust 原生支援 WebAssembly,所以它也能直接執行 WebAssembly。它的非同步操作不使用 libuv 這個庫,而是使用 Rust 語言的 Tokio 庫,來實現事件迴圈(event loop)。
4、
你可能會問,為什麼使用 Rust,而不是 C++(Node.js 的開發語言)?
主要原因是 Rust 提供了很多現成的模組,對 Deno 專案來說,可以節約很多開發時間。
5、
Deno 本身也是 Rust 的一個模組。如果你想在 Rust 裡面使用 V8 引擎,就可以載入 Deno。它等於是一個 V8 的包裝層,提供一些底層 API,讓你跟 V8 引擎互動。
6、
Deno 只有一個可執行檔案,所有操作都透過這個檔案完成。它支援跨平臺(Mac、Linux、Windows)。
7、
Deno 具有安全控制,預設情況下指令碼不具有讀寫許可權。如果指令碼未授權,就讀寫檔案系統或網路,會報錯。
必須使用引數,顯式開啟許可權才可以。
--allow-read
:開啟讀許可權,可以指定可讀的目錄,比如--allow-read=/temp
。--allow-write
:開啟寫許可權。--allow-net=google.com
:允許網路通訊,可以指定可請求的域,比如--allow-net=google.com
。--allow-env
:允許讀取環境變數。
8、
Deno 支援 Web API,儘量跟瀏覽器保持一致。
它提供 window 這個全域性物件,同時支援 fetch、webCrypto、worker 等 Web 標準,也支援 onload、onunload、addEventListener 等事件操作函式。
此外,Deno 所有的非同步操作,一律返回 Promise。
9、
Deno 只支援 ES 模組,跟瀏覽器的模組載入規則一致。沒有 npm,沒有 npm_modules 目錄,沒有require()
命令(即不支援 CommonJS 模組),也不需要package.json
檔案。
所有模組透過 URL 載入,比如import { bar } from "https://foo.com/bar.ts"
(絕對 URL)或import { bar } from './foo/bar.ts'
(相對 URL)。因此,Deno 不需要一箇中心化的模組儲存系統,可以從任何地方載入模組。
但是,Deno 下載模組以後,依然會有一個總的目錄,在本地快取模組,因此可以離線使用。
10、
由於 Deno 只支援從 URL 載入模組,導致 Node.js 的模組載入寫法都會失效。
import React from "react"; import { Box, Grid } from "@material-ui/core"; import { initializeApp } from "firebase/app";
上面的寫法在 Deno 裡面都是非法的。
Deno 的所有模組都要透過入口指令碼載入,不能透過模組名載入,所以必須帶有指令碼字尾名。
11、
Deno 原生支援 TypeScript 語言,可以直接執行,不必顯式轉碼。
它的內部會根據檔案字尾名判斷,如果是.ts
字尾名,就先呼叫 TS 編譯器,將其編譯成 JavaScript;如果是.js
字尾名,就直接傳入 V8 引擎執行。
12、
Deno 內建了開發者需要的各種功能,不再需要外部工具。打包、格式清理、測試、安裝、文件生成、linting、指令碼編譯成可執行檔案等,都有專門命令。
執行deno -h
或deno help
,就可以顯示 Deno 支援的子命令。
deno bundle
:將指令碼和依賴打包deno eval
:執行程式碼deno fetch
:將依賴抓取到本地deno fmt
:程式碼的格式美化deno help
:等同於-h
引數deno info
:顯示本地的依賴快取deno install
:將指令碼安裝為可執行檔案deno repl
:進入 REPL 環境deno run
:執行指令碼deno test
:執行測試
13、
Deno 的安裝可以參考官網首頁,但是你可以直接去 GitHub 倉庫的釋出頁,下載編譯好的可執行檔案(上圖)。
下載 Deno 以後,檢視一下版本。
$ deno --version deno 0.31.0 v8 8.1.108 typescript 3.7.2
命令列直接執行deno
,就會進入 REPL 環境。
$ deno > console.log(1,2,3) 1 2 3 undefined >
14、
下面,執行一個 TypeScript 的遠端指令碼,這是官網給出的例子。
$ deno run \ https://deno.land/std/examples/curl.ts \ https://example.com
上面例子中,Deno 執行遠端指令碼curl.ts
,用這個指令碼去抓取網址example.com
。但是,執行後報錯,表示沒有網路通訊的許可權。
我們給予 Deno 網路通訊的許可權,就可以順利執行。
$ deno run --allow-net \ https://deno.land/std/examples/curl.ts \ https://example.com
15、
現在,Deno 最新版本是 0.31。根據規劃,1.0 應該會在今年上半年釋出。
Deno 還處在密集開發中,功能不穩定,不建議用於生產環境。但是,它已經是一個可用的工具,大家可以多試用,熟悉它的用法。我相信,設計上的諸多優點,將會使它比 Node.js 更具優勢。
(完)