全新的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

相关文章
相关标签/搜索