全新的JavaScript runtime —— Deno 初體驗

本文首發於公衆號:符合預期的CoyPan

寫在前面

2020年5月13日,Deno終於正式發佈了。Deno是基於V8 JavaScript引擎和Rust編程語言的JavaScript和TypeScript運行時。它是由Node.js之父Ryan Dahl建立的,專一於安全性和生產力。javascript

爲何會有Deno

已經有了Node.js,爲何還要搞一個Deno呢?按Ryan Dahl在2018年的一個演講,他在設計Node.js的時候,犯了幾個"錯誤"(演講PPT我以前收集過,在公衆號回覆 前端學習 便可自行獲取)。這幾個"錯誤"是:前端

  1. 沒有堅持使用Promise。
  2. 沒有足夠的安全性。
  3. 構建系統沒有從GYP(node-gyp)切到GN。
  4. 繼續使用GYP,沒有提供Foreign Function Interface (FFI)的模式。
  5. package.json(依賴了npm)。
  6. 在任何地方均可以require('module')。
  7. package.json提供了錯誤的模塊概念。
  8. node_modules黑洞。
  9. require('module') 沒有加上擴展名'.js'。
  10. 默認加載index.js。

因而,Ryan Dahl決定開發一個新的JavaScript運行時。2年多後,Deno發佈了。官網地址:java

https://deno.land/node

Deno初體驗

趕忙來體驗一下Deno。typescript

安裝

macOS下直接curl便可:curl -fsSL https://deno.land/x/install/install.sh | shshell

更多安裝方式,請移步官網

下載完成後,將deno加入系統環境變量。首先npm

> vi ~/.bash_profile

接着,加入如下兩行:編程

export DENO_INSTALL="/Users/pankeyu/.deno"
export PATH="$DENO_INSTALL/bin:$PATH"

最後,執行:json

source ~/.bash_profile

而後,就能夠在命令行愉快的執行deno了:瀏覽器

1.png

命令行

deno命令行的用法和node差很少。

2.png

想要執行腳本,直接deno run ${script}便可。這裏的script,也能夠是一個線上文件。

直接執行代碼,能夠 deno eval "console.log(30933 + 404)"

deno支持如下命令:

bundle         Bundle module and dependencies into single file
cache          Cache the dependencies
completions    Generate shell completions
doc            Show documentation for a module
eval           Eval script
fmt            Format source files 
help           Prints this message or the help of the given subcommand(s)
info           Show info about cache or info related to source file
install        Install script as an executable
repl           Read Eval Print Loop
run            Run a program given a filename or url to the module
test           Run tests 
types          Print runtime TypeScript declarations
upgrade        Upgrade deno executable to given version

更多信息,可使用deno help查看。

從上面的bundle、test、fmt命令咱們能夠看出來:deno原生支持打包,測試,格式化

使用deno建立一個http server

咱們使用官方的例子:

// server.ts
import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

執行deno server.ts,命令行輸出:

3.png

能夠看到,直接報錯了。這是deno的安全機制,須要加上--allow-net這個參數,能夠訪問網絡。

執行deno --allow-net server.ts,命令行輸出以下:

> deno --allow-net server.ts
Compile file:///Users/pankeyu/Desktop/server.ts
Download https://deno.land/std@0.50.0/http/server.ts
Download https://deno.land/std@0.50.0/encoding/utf8.ts
Download https://deno.land/std@0.50.0/io/bufio.ts
Download https://deno.land/std@0.50.0/testing/asserts.ts
Download https://deno.land/std@0.50.0/async/mod.ts
Download https://deno.land/std@0.50.0/http/_io.ts
Download https://deno.land/std@0.50.0/io/util.ts
Download https://deno.land/std@0.50.0/path/mod.ts
Download https://deno.land/std@0.50.0/path/win32.ts
Download https://deno.land/std@0.50.0/path/posix.ts
Download https://deno.land/std@0.50.0/path/common.ts
Download https://deno.land/std@0.50.0/path/separator.ts
Download https://deno.land/std@0.50.0/path/interface.ts
Download https://deno.land/std@0.50.0/path/glob.ts
Download https://deno.land/std@0.50.0/path/_constants.ts
Download https://deno.land/std@0.50.0/path/_util.ts
Download https://deno.land/std@0.50.0/fmt/colors.ts
Download https://deno.land/std@0.50.0/testing/diff.ts
Download https://deno.land/std@0.50.0/path/_globrex.ts
Download https://deno.land/std@0.50.0/async/deferred.ts
Download https://deno.land/std@0.50.0/async/delay.ts
Download https://deno.land/std@0.50.0/async/mux_async_iterator.ts
Download https://deno.land/std@0.50.0/textproto/mod.ts
Download https://deno.land/std@0.50.0/http/http_status.ts
Download https://deno.land/std@0.50.0/bytes/mod.ts
http://localhost:8000/

瀏覽器打開http://localhost:8000/,就能夠看到輸出的Hello World了。

也許你會疑惑爲何要加一個--allow-net的參數。這是Deno的安全策略。

從上面的例子能夠看到,啓動腳本時,deno會實時下載依賴到本地,下載完成後,再執行腳本邏輯。當咱們control+C退出後,再次執行腳本,命令行輸出:

> deno --allow-net server.ts
http://localhost:8000/

這一次不會再下載依賴了。

deno在第一次下載後,將依賴保存到了本地,而且這些依賴代碼自己對用戶是不可見的。這點和Node.js的node_modules徹底不一樣。

若是咱們想從新下載依賴,須要在執行腳本的時候加上--reload:deno --allow-net --reload server.ts

若是咱們想查看腳本的依賴樹,須要執行deno info server.ts :

> deno info server.ts 
local: /Users/pankeyu/Desktop/server.ts
type: TypeScript
compiled: /Users/pankeyu/Library/Caches/deno/gen/file/Users/pankeyu/Desktop/server.ts.js
map: /Users/pankeyu/Library/Caches/deno/gen/file/Users/pankeyu/Desktop/server.ts.js.map
deps:
file:///Users/pankeyu/Desktop/server.ts
  └─┬ https://deno.land/std@0.50.0/http/server.ts
    ├── https://deno.land/std@0.50.0/encoding/utf8.ts
    ├─┬ https://deno.land/std@0.50.0/io/bufio.ts
    ...

上面的簡單例子還體現出下面幾點:

  1. deno原生支持了typescript。實際上,deno內置了ts引擎,會將ts代碼解析爲js代碼後,交給v8運行。
  2. deno原生支持了top-level-await。
  3. 啓動上面的示例腳本時,若是沒有加上--allow-net這個flag,是會報錯的。能夠看出deno在安全方面的考量。
deno的https server性能

Deno 是一個合適的異步服務器,每秒 25k 請求足以知足大多數目的,此外,因爲廣泛使用 Promise,Deno 須要有更好的尾部延遲。目前 Deno HTTP 服務器每秒處理約 25 000 個請求,最大延遲爲 1.3 毫秒,與之相比,Node 程序每秒處理 34 000 個請求,最大延遲介於 2 到 300 毫秒之間。

這樣看來,做者認爲 Deno 的 HTTP 服務器還有更多的性能優點,並表示但願在未來的版本中實現這一目標。

deno的http server性能能夠在這裏查看:https://deno.land/benchmarks

deno中的依賴與模塊

在Node中,提供了許多的內置模塊,如:

const fs = require('fs');
const path = require('path');
...

在deno中,也提供了很多的內置模塊,可是並不支持Node同樣的引入方式,而是掛在Deno這個全局變量上。看一個例子:

// denoFs.js
const readFile = Deno.readFile;
const serverBuffer = await readFile('./server.ts');
console.log(serverBuffer);

執行腳本:

> deno run --allow-read  denoFs.js
Uint8Array(213) [
  105, 109, 112, 111, 114, 116,  32, 123,  32, 115, 101, 114, 118, 101,
   32, 125,  32, 102, 114, 111, 109,  32,  34, 104, 116, 116, 112, 115,
   58,  47,  47, 100, 101, 110, 111,  46, 108,  97, 110, 100,  47, 115,
  116, 100,  64,  48,  46,  53,  48,  46,  48,  47, 104, 116, 116, 112,
   47, 115, 101, 114, 118, 101, 114,  46, 116, 115,  34,  59,  10,  99,
  111, 110, 115, 116,  32, 115,  32,  61,  32, 115, 101, 114, 118, 101,
   40, 123,  32, 112, 111, 114, 116,  58,  32,  56,  48,  48,  48,  32,
  125,  41,
  ... 113 more items
]

deno的模塊化徹底遵循了es module。從前面的例子能夠看出,deno能夠直接import線上的資源包。對於本地資源,使用本地路徑引入便可,可是必需要帶上資源後綴(.ts,.js)。import就是申明依賴。

// world.ts
export const world:string = 'world';

// hello.ts
import { world } from './world.ts';
console.log(`Hello ${world}`);

執行腳本deno run hello.ts

> deno run hello.ts
Compile file:///Users/pankeyu/Desktop/hello.ts
Hello world
deno的內置工具

前面咱們提到,deno原生支持打包,測試,格式化等。咱們來試一試吧。

  • 打包

咱們使用上面的denoFs.js做爲例子。

> deno bundle denoFs.js denoFs.output.js
Bundling file:///Users/pankeyu/Desktop/deno/denoFs.js
Emitting bundle to "denoFs.output.js"
2482 bytes emmited.

最終輸出了一個denoFs.output.js文件,大概長成下面的樣子,這個js文件也是能夠直接由deno運行的。

// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.

// This is a specialised implementation of a System module loader.

// @ts-nocheck
/* eslint-disable */
let System, __instantiateAsync, __instantiate;

(() => {
  const r = new Map();

  System = {
    register(id, d, f) {
      r.set(id, { d, f, exp: {} });
    },
  };

  async function dI(mid, src) {
    ...
  }

  function gC(id, main) {
    ...
  }

  function gE(exp) {
    ...
  }

  function rF(main) {
    ...
  }

  async function gExpA(id) {
    ...
  }

  function gExp(id) {
    ...
  }

  __instantiateAsync = async (m) => {
    ...
  };

  __instantiate = (m) => {
    ...
  };
})();

"use strict";
const readFile = Deno.readFile;
const serverBuffer = await readFile("./server.ts");
console.log(serverBuffer);

__instantiate("denoFs");
  • 測試

咱們使用上面的world.ts做爲例子。

// world.ts
export const world:string = 'world';

deno會從當前目錄開始,逐級向上讀取文件名爲{*_,}test.{js,ts,jsx,tsx}文件做爲測試文件。咱們先寫好測試用例:

// world.test.ts
import { world } from "./world.ts";
Deno.test("env", () => {
  if (world !== 'world') {
    throw Error("wrong!");
  }
});

執行 deno test:

Compile file:///Users/pankeyu/Desktop/deno/.deno.test.ts
running 1 tests
test env ... ok (4ms)

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (4ms)
  • 格式化代碼

假設咱們待格式化的代碼爲:

// server.ts
import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
  const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const  req of   s) {
        req.respond({ body: "Hello World\n" })
}

執行deno fmt server.ts後,代碼格式化完成:

// server.ts
import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
  req.respond({ body: "Hello World\n" });
}

總結

經過上面的探索,結合以前Ryan Dahl提到的Nodejs的"設計錯誤",能夠稍微總結一下deno了。

一、deno從新實現了模塊化機制,採用去中心化的設計,支持直接import線上的資源,再也不像Node同樣依賴npm,擺脫了node_modules。同時,deno官方也提供了一個第三方庫倉庫:https://deno.land/std/

二、deno的內置模塊是掛在全局變量上的。

三、deno內置了typescript解析引擎,原生支持typescript。而且,deno也在擁抱W3C的規範(deno支持fetch)。

四、deno默認是安全的。從上面的例子中就能夠看出,想要訪問網絡,訪問文件系統等,都須要加上特定的參數才能夠。

五、deno原生支持打包,測試,代碼格式化等操做,旨在提升生產效率。

deno能夠說是在重塑以前Nodejs的開發模式,其設計思想相比於Node.js,確實有進步的地方。對比做者以前提到的幾條Node.js的"設計錯誤",deno一一解決了。

deno讓人眼前一亮的去中心化模塊依賴,或許可讓前端cdn、生產環境自動化部署等技術獲得進一步發展。不過deno想要達到Node.js的穩定性以及繁榮的生態,deno還有很長的路要走。

寫在後面

本文經過一個例子,不完整地對deno進行了介紹。Ryan Dahl大號練廢了,又開了一個小號修煉。那麼,這一次,你認爲deno會火嗎?

最新的.png

相關文章
相關標籤/搜索