Node 之父 Ryan Dahl 近日在柏林 JS 大会上发表了主题演讲,这也是 Ryan Dahl
做的第二次关于 JS 的公开演讲,第一次是在 2009 年,当时是宣布 Node
项目诞生,而这一次,演讲主题却是 “Design Mistakes in Node” 。 

  • 不允许将任意本地函数绑定至 V8 当中。
  • 所有系统调用都将通过消息传递完成(protobuf 序列化)。
  • 两项原生函数:send 与 recv。
  • 这既简化了设计流程,又使得系统更易于审计。

澳门葡萄京官方网站 1

澳门葡萄京官方网站 2

这几点很大程度上体现出了node和deno在设计本质上的区别,同时这几点体现了deno的安全性(利用
JavaScript 本身即为安全沙箱这一事实)

这几天假期,我学习了一下 Deno[1]。它是 Node.js
的替代品。有了它,将来可能就不需要 Node.js 了。

目前网络上还没有该演讲的相关视频,仅公布了
PPT(地址)。根据 PPT
内容可以看到,Ryan Dahl 在前半段主要讲述了过去他在设计 Node
时犯的一些错误,包括安全性、构建系统、package.json、node_modules、index.js
等等,并表示 Node 存在的种种不足导致有许多严重 bug 问题且不可回避。

  • 允许从GO程序执行JavaScript
  • 只允许GO和V8之间的消息传递(传统:暴露C++函数作为函数在JavaScript。)
  • 维护一个安全的JS沙箱
  • JS中只允许绑定3个函数:$send $print()

这篇文章就是 Deno 的一个初步介绍,尝试回答为什么 Node.js
不能满足需要,以及 Deno 能够带给我们什么?

Rather I will complain about all the warts in Node. Bugs are never so
obvious as when you’re the one responsible for them. At times Node is
like nails on chalkboard to me. It could have been so much nicer.

澳门葡萄京官方网站 3deno
架构图(Parsa Ghadimi绘制)

以下内容主要基于 Bert Belder[2]和 Ryan Dahl[3]的最新演讲。

在后半段,Ryan Dahl 则主要阐述了新项目 Deno 的目标和规划。Deno 将发挥
2018 年的技术优势,朝着安全性,简化模块系统,将 TypeScript
编译器内置于可执行文件当中、Misc 等目标前进。

从图中可以清晰的看出,V8worker2是v8和Go之间实现调用的核心组件

0、

澳门葡萄京官方网站 4

澳门葡萄京官方网站 5v8worker2架构图

进入主题之前,先说一下 Deno 这个词怎么发音。

澳门葡萄京官方网站,PPT 地址:

可以看出V8worker2 是通过binding C++
模块进行绑定V8,bingding暴露了基础操作方法:$v8_init()
、$worker_load()、$worker_send_bytes()、$worker_dispose()…提供给GO
进行调用

两种发音,“德诺”和“蒂诺”,我都听到过。看起来,“蒂诺”这个发音应该更合适一些,因为
Deno 的标志是一只恐龙。恐龙的英文缩写正是 dino。

(文/开源中国)    

//binding.hconst char* worker_version();void worker_set_flags(int* argc, char** argv);void v8_init();worker* worker_new(int table_index);int worker_load(worker* w, char* name_s, char* source_s);const char* worker_last_exception(worker* w);int worker_send_bytes(worker* w, void* data, size_t len);void worker_dispose(worker* w);void worker_terminate_execution(worker* w);

1、

通过Golang的GC提供的CGO模块调用C语言暴露的方法,就可以实现GO和V8之间的通信了:

Deno 是 Ryan Dahl 在 2017 年创立的。

  1. 创建一个实例:v8worker2.New(ReceiveMessageCallback)
  2. 加载执行JS: worker.Load(scriptName,codeString)

Ryan Dahl 也是 Node.js 的创始人,从 2007 年一直到 2012 年,他后来把
Node.js 移交给了其他开发者,不再过问了,转而研究人工智能。

他始终不是很喜欢 Python 语言,久而久之,就想搞一个 JavaScript
语言的人工智能开发框架。等到他再回过头捡起
Node.js,发现这个项目已经背离了他的初衷,有一些无法忽视的问题。

// worker.gopackage v8worker2import "C"...func recvCb(buf unsafe.Pointer, buflen C.int, index workerTableIndex) C.buf { ...}func New(cb ReceiveMessageCallback) *Worker { ... initV8Once.Do { C.v8_init}func (w *Worker) Load(scriptName string, code string) error { ... r := C.worker_load(w.worker.cWorker, scriptName_s, code_s)...}func (w *Worker) SendBytes(msg []byte) error { ... r := C.worker_send_bytes(w.worker.cWorker, msg_p, C.size_t)}

2、

  • 实现Js中的console.log() 方法
  • Js发送数据给Go
  • Go发送数据给Js

首先,过去五六年,JavaScript 语言脱胎换骨,ES6
标准引入了大量新的语法特性。其中,影响最大的语法有两个:Promise 接口和
ES 模块。

Node.js 对这两个新语法的支持,都不理想。由于历史原因,Node.js
必须支持回调函数,导致异步接口会有 Promise
和回调函数两种写法;同时,Node.js 自己的模块格式 CommonJS 与 ES
模块不兼容,导致迟迟无法完全支持 ES 模块。

// hello.gopackage mainimport ( "fmt" "github.com/ry/v8worker2")func main() { worker := v8worker2.New // 实现JS的console.log 方法 err := worker.Load("hello.js", ` this["console"] = { log { V8Worker2.print } }; console.log("Hello World"); `) if err != nil { fmt.Println } // 发送数据给GO err = worker.Load("sendData.js", ` V8Worker2.send(new ArrayBuffer if err != nil { fmt.Println } // 发送数据给JS err = worker.Load("recvData.js", ` V8Worker2.recv(function { const len =msg.byteLength; console.log("recv data from go,length: "+len); }); `) if err != nil { fmt.Println } err = worker.SendBytes([]byte}func recv(buf []byte) []byte { fmt.Println("recv data from js,length:", len return nil}

其次,Node.js 的模块管理工具 npm,逻辑越来越复杂;模块安装目录
npm_modules 极其庞杂,难以管理。Node.js
也几乎没有安全措施,用户只要下载了外部模块,就只好听任别人的代码在本地运行,进行各种读写操作。

在控制台运行: go run hello.go

再次,Node.js
的功能也不完整,导致外部工具层出不穷,让开发者疲劳不堪:webpack,babel,typescript、eslint、prettier……

澳门葡萄京官方网站 6运行结果

3、

需要运行测试代码,可以直接访问我的github :deno 案例源码

由于上面这些原因,Ryan Dahl 决定放弃
Node.js,从头写一个替代品,彻底解决这些问题。deno 这个名字就是来自 Node
的字母重新组合,表示“拆除 Node.js”。

参考资料

跟 Node.js 一样,Deno
也是一个服务器运行时,但是支持多种语言,可以直接运行
JavaScript、TypeScript 和 WebAssembly 程序。

  • Ryan Dahl 对 v8worker 的演讲PPT
  • justjavac: Deno 并不是下一代 Node.js
  • v8worker2 github

它内置了 V8 引擎,用来解释 JavaScript。同时,也内置了 tsc 引擎,解释
TypeScript。它使用 Rust 语言开发,由于 Rust 原生支持
WebAssembly,所以它也能直接运行 WebAssembly。它的异步操作不使用 libuv
这个库,而是使用 Rust 语言的 Tokio[4]库,来实现事件循环。

4、

你可能会问,为什么使用 Rust,而不是 C++?

主要原因是 Rust 提供了很多现成的模块,对 Deno
项目来说,可以节约很多开发时间。

5、

Deno 本身也是 Rust 的一个模块。如果你想在 Rust 里面使用 V8
引擎,就可以加载 Deno。它等于是一个 V8 的包装层,提供一些底层
API,让你跟 V8 引擎互动。

6、

Deno 只有一个可执行文件,所有操作都通过这个文件完成。它支持跨平台。

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命令,也不需要package.json文件。

所有模块通过 URL
加载,比如import { bar } fromimport { bar } from './foo/bar.ts'。因此,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 -hdeno 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 的安装可以参考官网首页[5],但是你可以直接去 GitHub
仓库的发布页[6],下载编译好的可执行文件。

下载 Deno 以后,查看一下版本。

$ deno --versiondeno 0.31.0v8 8.1.108typescript 3.7.2

命令行直接运行deno,就会进入 REPL 环境。

$ denogt; console.log(1,2,3)1 2 3undefinedgt;

14、

下面,运行一个 TypeScript 的远程脚本,这是官网给出的例子[7]。

$ deno run  

上面例子中,Deno
执行远程脚本curl.ts,用这个脚本去抓取网址example.com。但是,运行后报错,表示没有网络通信的权限。

我们给予 Deno 网络通信的权限,就可以顺利执行。

$ deno run --allow-net  

15、

现在,Deno 最新版本是 0.31。根据规划,1.0 应该会在今年上半年发布。

Deno
还处在密集开发中,功能不稳定,不建议用于生产环境。但是,它已经是一个可用的工具,大家可以多试用,熟悉它的用法。我相信,设计上的诸多优点,将会使它比
Node.js 更具优势。

参考资料

[1]

Deno:

[2]

Bert Belder:

[3]

Ryan Dahl:

[4]

Tokio:

[5]

官网首页:

[6]

发布页:

[7]

例子: