简介

Deno简略说是Node.js的替代品,是Node.js之父Ryan Dahl 为挽回Node.js的谬误而开发的。

Node.js存在的问题有:

  1. npm包治理(node_modules)简单。
  2. 历史起因导致的api保护,比方晚期变态的callback设置。
  3. 没有安全措施,用户只有下载了内部模块,就只好放任他人的代码在本地运行,进行各种读写操作。
  4. 性能不欠缺,导致各种工具层出不穷,比方webpack、babel等。

因为下面这些起因,Ryan Dahl 决定放弃 Node.js,从头写一个替代品,彻底解决这些问题。
deno 这个名字就是来自 Node 的字母重新组合(Node = no + de),示意"拆除 Node.js"(de = destroy, no = Node.js)。

跟 Node.js一样,Deno 也是一个服务器运行时,然而反对多种语言,能够间接运行 JavaScript、TypeScript 和 WebAssembly 程序。

  • 它内置了 V8 引擎,用来解释 JavaScript。同时,也内置了 tsc 引擎,解释 TypeScript。
  • 它应用 Rust 语言开发,因为 Rust 原生反对 WebAssembly,所以它也能间接运行 WebAssembly。
  • 它的异步操作不应用 libuv 这个库,而是应用 Rust 语言的 Tokio 库,来实现事件循环(event loop)。

它的架构如下图所示:

阐明:
1、Rust 是由 Mozilla 主导开发的通用、编译型编程语言。设计准则为 “平安、并发、实用”,反对函数式、并发式、过程式以及面向对象的编程格调。Deno 应用 Rust 语言来封装 V8 引擎,通过 libdeno 绑定,咱们就能够在 JavaScript 中调用隔离的性能。
2、Tokio 是 Rust 编程语言的异步运行时,提供异步事件驱动平台,构建疾速,牢靠和轻量级网络应用。利用 Rust 的所有权和并发模型确保线程平安。Tokio 构建于 Rust 之上,提供极快的性能,使其成为高性能服务器应用程序的现实抉择。在 Deno 中 Tokio 用于并行执行所有的异步 IO 工作。
3、V8 是一个由 Google 开发的开源 JavaScript 引擎,用于 Google Chrome 及 Chromium 中。V8 在运行之前将JavaScript 编译成了机器代码,而非字节码或是解释执行它,以此晋升性能。更进一步,应用了如内联缓存(inline caching)等办法来进步性能。有了这些性能,JavaScript 程序与 V8 引擎的速度媲美二进制编译。在 Deno 中,V8 引擎用于执行 JavaScript 代码。

劣势

Deno凭什么来吸引开发者改换门庭,转投它的怀抱呢?于我而言,次要有以下几点:

  • 人造反对ts。如果用Node.js,须要咱们手动将ts编译成js,或者应用ts-node这种第三方工具。Deno不须要配置,开箱即用。
  • 内存平安。相较于Node.js,它的性能劣势并不显著,因为就是js换了个运行环境而已,Rust并不见得比C++更高效,不过Rust会人造让它的内存平安和外围代码的健壮性更有保障些。
  • 平安机制。除非明确启用,否则没有文件,网络或环境拜访权限。对于运行权限的划分,能清晰晓得你的程序须要领有哪些权限,对网络资源没有信赖的同学能够释怀了。
  • 没有历史包袱。不像Node.js一开始自定义了commonjs标准,Deno间接反对ES Modules。没有回调天堂,代码构造更清晰,利于tree shaking;也能够间接应用顶级await。
  • 去中心化。不必每个工程都装置一大堆node_modules,有个线上的url就能获取代码。
  • 内置浏览器API。Deno实现了fetch、FormData、WebSocket等浏览器的API,尽管被某些开发者诟病,但对前端开发者而言是真的敌对,比方进行接口调用,与在浏览器里应用简直没有区别。
  • 全家桶似的服务,比方打包、格式化、代码校验,能够让你更专一业务,而不是成为某工具的配置工程师。

毛病

Deno的毛病也很显著:

  • 没有针对Node.js压倒性的性能劣势,所以对以上劣势不感兴趣的开发者而言,就没有吸引力了。

    Deno 是一个适合的异步服务器,每秒 25k 申请足以满足大多数目标,此外,因为广泛应用 Promise,Deno 有更好的尾部提早。目前 Deno HTTP 服务器每秒解决约 25 000 个申请,最大提早为 1.3 毫秒,与之相比,Node 程序每秒解决 34 000 个申请,最大提早介于 2 到 300 毫秒之间。
    能够看出,Deno的劣势在于提早低,但并没有压倒性劣势。
  • 无奈齐全继承Node.js的生态。尽管Deno提供了几款CDN服务,能够将旧的npm包转换为Deno可应用的格局,但并非所有npm包都能转换胜利。因为Node.js仍有局部API官网没有实现,如果某个偏底层的包转换失败,所有依赖它的包都无奈胜利。所以,你很有可能在开发的某个环节须要造轮子。当然,这对于喜爱钻研技术的同学而言反而是个利好。
  • 去中心化后,国内就没有方法像npm做镜像,官网搭建deno.land来存储github上各种资源,但目前只反对github上公有资源。想要援用公有文件,就必须本人搭建服务器,在CICD中构建的话,又有额定的技术难点须要解决。

    装置

1、 Mac/Linux

鉴于国内的网速起因,@justjavac 大佬搞了国内的镜像减速:

curl -fsSL https://x.deno.js.cn/install.sh | sh

我用Linux部署后,报没有glibc-2.18这个版本依赖。

按网上教程,又装置了一下:

curl -O http://ftp.gnu.org/gnu/glibc/glibc-2.18.tar.gztar zxf glibc-2.18.tar.gzcd glibc-2.18/mkdir buildcd build/../configure --prefix=/usrmake -j2make install

后两步比较慢,不要退出。

2、Windows

https://github.com/denoland/deno/releases

间接在这里下载exe文件吧。

当然,Mac与Linux也能够这样装置,但最新的Mac零碎可能会因为平安权限而禁止程序应用,须要参考这里配置下。

设置环境变量

你可能还须要设置环境变量。以下是Mac/Linux命令行操作:

echo 'export DENO_INSTALL="$HOME/.deno"export PATH="$DENO_INSTALL/bin:$PATH"export DENO_DIR=$HOME/.deno' >> ~/.bash_profilesource ~/.bash_profile

测试

deno --version

降级

deno upgrade

或降级到特定版本,例如v1.3.0

deno upgrade --version 1.3.0

平安机制

Deno 具备安全控制,默认状况下脚本不具备读写权限。如果脚本未受权,就读写文件系统或网络,会报错。

必须应用参数,显式关上权限才能够。

--allow-read:关上读权限,能够指定可读的目录,比方--allow-read=/temp。--allow-write:关上写权限。--allow-net=google.com:容许网络通信,能够指定可申请的域,比方--allow-net=google.com。--allow-env:容许读取环境变量。--allow-run:容许运行子过程。

要应用不稳固的新个性,须要这样:

--unstable

还有新加的--allow-ffi--allow-hrtime,个别用不到,这里不再赘述,请情参考官网文档。

运行

应用deno run来运行程序,当然,个别须要加上下面的平安机制。
如果你的程序什么权限都没应用,只是上面这种:

console.log('app start');

那么间接用deno run main.ts就能够了。
留神,咱们举荐应用ts,但也能够运行js。

如果须要读取文件,比方

const decoder = new TextDecoder("utf-8");const data = await Deno.readFile("hello.txt");console.log(decoder.decode(data));

天然得这样:deno run --allow-read main.ts

如果须要网络,比方开启一个tcp:

/** * echo_server.ts */import { copy } from "https://deno.land/std@0.106.0/io/util.ts";const listener = Deno.listen({ port: 8080 });console.log("listening on 0.0.0.0:8080");for await (const conn of listener) {  copy(conn, conn).finally(() => conn.close());}

你必须:deno run --allow-net main.ts

如果懒得管理权限,则应用-Adeno run -A main.ts),那就后果自负了。

当然,也能够运行近程服务器上的文件,比方官网的hello world

deno run https://deno.land/std/examples/welcome.ts

更新模块

默认代码中援用中的ts或js文件,会缓存到本地。如果你援用的文件url中没有带版本号,那么就是最新版的代码。这时,你想要更新代码,那么你须要这样操作:

deno cache --reload my_module.ts

更新线上与本地文件:

deno cache --reload=https://deno.land/std@0.106.0 my_module.ts

更新线上某个文件与某文件夹:

deno cache --reload=https://deno.land/std@0.106.0/fs/copy.ts,https://deno.land/std@0.106

锁定文件校验

举荐建一个deps.ts文件,来治理内部援用的文件。其实就是起到相似package-lock.json的作用,治理援用的文件与版本。

// deps.tsexport { xyz } from "https://unpkg.com/xyz-lib@v0.9.0/lib.ts";

而后用deno cache 生成一个lock.json文件:

# Create/update the lock file "lock.json".deno cache --lock=lock.json --lock-write deps.ts

另一台机器克隆下代码,运行后:

# Download the project's dependencies into the machine's cache, integrity# checking each resource.deno cache --reload --lock=lock.json deps.ts# Done! You can proceed safely.deno test --allow-read src

导入地图

能够应用带有--import-map=<FILE>CLI 标记的导入映射,这样开发时应用体验相似于Node.js了。

不过如果是公布给他人应用的模块,不倡议用它,因为这须要他人也把你的map文件复制过去。

例:import_map.json

{     "imports": {          "fmt/": "https://deno.land/std@0.106.0/fmt/"     }}

main.ts:

import { red } from "fmt/colors.ts"; console.log(red("hello world"));

而后:

deno run --import-map=import_map.json main.ts

来自CDN的包

目前,举荐将共享的代码公布在官网搭建的平台https://deno.land/x/上,它的公布也很简略,在你的github仓库里配置一个web hook,就能够了。

具体详见疏导,非常简单,这里不再赘述。

esm.sh

https://esm.sh/是个转换npm包为Deno应用的线上地址,它应用esbuild进行转换,速度很快。比方咱们要应用React,能够这样间接应用:

import React from "https://esm.sh/react";export default class A extends React.Component {  render() {    return <div></div>;  }}

当然也反对版本号:

import React from "https://esm.sh/react@17.0.2";

Skypack

https://www.skypack.dev/也是相似:

import React from "https://cdn.skypack.dev/react";export default class A extends React.Component {  render() {    return <div></div>;  }}

装置脚本

应用deno install能够不便地装置一个脚本,相似于npm i -g xxx模块。

$ deno install --allow-net --allow-read https://deno.land/std@0.106.0/http/file_server.ts[1/1] Compiling https://deno.land/std@0.106.0/http/file_server.ts✅ Successfully installed file_server./Users/deno/.deno/bin/file_server

打包

打包为js

能够打包线上或本地文件为一个js文件:

deno bundle https://deno.land/std@0.106.0/examples/colors.ts colors.bundle.jsdeno bundle main.ts main.js

打包后的文件是能够运行的:

deno run -A bundle.js

因为导出的是个ES模块化的代码,甚至能够放到浏览器里应用:

<script type="module" src="website.bundle.js"></script>

或者在另一个js里援用:

<script type="module">  import * as website from "website.bundle.js";</script>

编译为可执行文件

deno compile https://deno.land/std/examples/welcome.ts

也能够通过增加--targetCLI 标记为其余平台编译二进制文件,比方在linux服务器上打包一个exe文件供windows应用。详见此处。

生成文档

deno doc后跟一个或多个源文件的列表将打印每个模块导出成员的 JSDoc 文档。
例如,给定一个add.ts蕴含以下内容的文件:

/** * Adds x and y. * @param {number} x * @param {number} y * @returns {number} Sum of x and y */export function add(x: number, y: number): number {  return x + y;}

运行 deno doc命令,将函数的 JSDoc 正文打印到stdout。

应用--json标记以 JSON 格局输入文档。这种 JSON 格局由 deno doc 网站应用,用于生成模块文档。

代码格调

格式化

应用deno fmt。它会将代码格式化为官网举荐的样子,其实起到的相似eslint --fix的成果。

# format all JS/TS files in the current directory and subdirectoriesdeno fmt# format specific filesdeno fmt myfile1.ts myfile2.ts# check if all the JS/TS files in the current directory and subdirectories are formatteddeno fmt --check# format stdin and write to stdoutcat file.ts | deno fmt -

代码校验

应用deno lint。有了它,就不用再应用eslint了。

# lint all JS/TS files in the current directory and subdirectoriesdeno lint# lint specific filesdeno lint myfile1.ts myfile2.ts# print result as JSONdeno lint --json# read from stdincat file.ts | deno lint -

编码格调指南

参见官网指南,举几条对咱们有用的:

  • 应用 TypeScript 而不是 JavaScript
  • 在文件名中应用下划线,而不是破折号
  • 不要应用文件名index.ts/ index.js。入口文件应用mod.ts/mod.js
  • 导出的函数:最多 2 个参数,将其余的放入选项对象中
  • 顶级函数不应应用箭头语法。顶级函数应该应用function关键字。箭头语法应该仅限于闭包

调试

像Node.js一样,Deno程序也能够在chrome浏览器中调试,具体就不介绍了。
次要说下vscode中调试。

须要在我的项目根目录下配置.vscode/launch.json文件,内容大略如下:

{    "version": "0.2.0",    "configurations": [        {            "name": "Deno: Run",            "request": "launch",            "type": "pwa-node",            "program": "src/main.ts", // 具体文件门路            "cwd": "${workspaceFolder}",            "runtimeExecutable": "deno",            "runtimeArgs": [                "run",                "--unstable",                "--inspect",                "--allow-all"            ],            "attachSimplePort": 9229        }    ]}

如果你的入口文件有多个,写起来比拟麻烦,能够这样:

{  "version": "0.2.0",  "configurations": [    {      "name": "Deno",      "type": "pwa-node",      "request": "launch",      "cwd": "${workspaceFolder}",      "runtimeExecutable": "deno",      "runtimeArgs": [        "run",        "--inspect-brk",        "-A",        "--config",        "tsconfig.json",        "--unstable",        "${file}"      ],      "attachSimplePort": 9229    }  ]}

详情能够参考这里。

各大支流库代替计划

Electron

咱们晓得,当初Electron是个优良的桌面端解决方案,咱们罕用的vscode就是用它写的,基于Node.js + Chromium 的 Electron 来依靠 Web 技术栈创立桌面应用程序。那么咱们能够在 Deno 下应用 Electron 吗?或者还有其它更多抉择吗?
答案是现在的 Electron 还远远不能运行在 Deno 上,咱们必须寻找其它的解决方案。自从 Deno 抉择用 Rust 语言构建其内核后,咱们能够应用 Rust 生态上的 Web View @Bosop/web-view 来在 Deno 上运行桌面利用。
于是 @eliassjogreen/deno_webview 应运而生。

import { Webview } from "https://deno.land/x/webview/mod.ts";const html = `  <html>  <body>    <h1>Hello from deno v${Deno.version.deno}</h1>  </body>  </html>`;const webview = new Webview(  { url: `data:text/html,${encodeURIComponent(html)}` },);await webview.run();

pm2

咱们应用pm2来守护Node.js过程,能够让代码运行在后盾。事实上,它也能够运行 Node.js 之外的的脚本语言。
所以,如果要运行deno,须要创立一个app.sh文件:

#!/bin/bashdeno run -A main.ts

再应用pm2启动:

pm2 start ./app.sh

Express / koa

这俩Node.js祖师爷级别的web框架,在deno里当然少不了,Express不倡议应用,举荐用koa的高仿oak吧。

import { Application } from "https://deno.land/x/oak/mod.ts";const app = new Application();app.use((ctx) => {  ctx.response.body = "Hello World!";});await app.listen({ port: 8000 });

nestjs

nestjs 是一个用于构建高效、可扩大的Node.js服务器端应用程序的开发框架。它利用 JavaScript 的渐进加强的能力,应用并齐全反对 TypeScript(依然容许开发者应用纯 JavaScript 进行开发),并联合了 OOP (面向对象编程)、FP (函数式编程)和 FRP (函数响应式编程)。从它身上,咱们能看到Spring和Angular的影子。

咱们能够利用nestjs轻松构建一个REST服务,以Controlller的用法为例:

@Controller('cats')export class CatsController {  @Get()  findAll(): string {    return 'This action returns all cats';  }}

官网目前并没有打算做deno的版本,所以我基于oak实现了一个简版的oak_nest,包含Controller、平安守卫和各类参数装璜器。感兴趣的同学能够试用,有问题随时能够找我。

罕用数据库

这里列举俩,其它在这里本人搜寻。

MongoDB

有deno_mongo,有罕用的API。当然,它没有mongoose那么丰盛,更简单的性能可能就须要你来本人造轮子了。
如果想应用Schema,能够试用deno_mongo_schema,它是基于deno_mongo的扩大。

Redis

有deno-redis。

Nodemon

咱们应用nodemon来监控Node.js程序,能够主动重启服务,在开发阶段十分不便。
deno中替代品为denon,咱们须要保护一个scripts.json,起到Node.js中package.json里script命令的作用。

一个简略的样例领会下:

# 应用denon dev启动服务scripts:  dev:    cmd: "deno run example/main.ts"    desc: "run example main.ts file"    allow:      - net      - env      - write    unstable: false    lock: lock.json    log: info    env:      PORT: "1000"  build:    cmd: "deno bundle example/main.ts example/main.js"    watch: false

测试工具

在Node.js里,咱们有jest、Jasmine等库,而deno能够应用官网测试std库测试:https://deno.land/std/testing。

import { assertStrictEq } from 'https://deno.land/std/testing/asserts.ts'Deno.test('My first test', async () => {  assertStrictEq(true, false)})

运行测试:

➜  deno test

nvm

咱们应用nvm来治理Node.js版本,在Deno中有dvm。

在docker中运行

Dockerhub 现已提供 Deno 的官网 Docker 镜像。

  • Alpine Linux: denoland/deno:alpine
  • Centos: denoland/deno:centos
  • Debian: denoland/deno:debian(默认)
  • Distroless: denoland/deno:distroless
  • Ubuntu: denoland/deno:ubuntu

要在 Docker 外部运行 Deno,咱们能够创立以下 Dockerfile

FROM denoland/deno:1.16.2# The port that your application listens to.EXPOSE 1993WORKDIR /app# Prefer not to run as root.USER deno# Cache the dependencies as a layer (the following two steps are re-run only when deps.ts is modified).# Ideally cache deps.ts will download and compile _all_ external files used in main.ts.COPY deps.ts .RUN deno cache deps.ts# These steps will be re-run upon each file change in your working directory:ADD . .# Compile the main app so that it doesn't need to be compiled each startup/entry.RUN deno cache main.tsCMD ["run", "--allow-net", "main.ts"]

而后这样应用:

docker build -t app . && docker run -it -p 1993:1993 app

详见这里。

以下是一个咱们我的项目中的样例:

FROM denoland/deno:alpine-1.16.2EXPOSE 3000WORKDIR /app# Prefer not to run as root.RUN chown -R deno /appRUN chmod 755 /appADD . .ENV DENO_DIR=deno-dirCMD deno run --allow-net --allow-env --allow-write --allow-read --config tsconfig.json --unstable mod.ts

这里设置了DENO_DIR,起因是在后面CICD的流水线中,曾经缓存了工程的依赖包在deno-dir目录下,在这里就不用再下载了。
最佳计划其实是运行打包后的js文件,这样服务的启动速度能快许多,遗憾的是官网bundle有个全局命名抵触的bug仍未修复。

据说1.17.0曾经修复了,但因为其它工具包的兼容问题,我还没有验证。

总结

本文次要是为了科普Deno,介绍了它的劣势与毛病,装置应用与罕用Node.js库的代替计划。它相较于Node.js,没有历史包袱,所以能够轻装上阵。

但想要代替Node.js成为前端人员的标配,就不是一年两年能够做到的事件了。除非Deno 真的推出了 Node.js 无奈复制的弱小性能,那才有可能会扭转游戏规则。

Deno当初正在致力做的是兼容Node.js的API,升高后者开发者的迁徙难度,这样才有可能将Node.js长达十余年的生态继承过去。

尽管Deno没有达到石破天惊的成果,但仍是个优良的工具,我看好它。心愿大家都有趣味玩一玩。

参考:

  • 20 分钟入门 deno
  • Deno手册【官网】
  • 从 Node 到 Deno:摸索各大支流库代替计划
  • 了不起的 Deno 入门与实战