写点什么

初探 Deno.js

用户头像
★忆先★
关注
发布于: 2021 年 06 月 17 日

Node.js 的作者 Ryan Dahl 在 2018 年 JSConf EU 上发布了一篇演讲:10 Things I Regret About Node.js,讲述了 他认为自己在设计 Node.js 时的十个(实际上演讲中只提到七个)失误,包括:没有坚持使用 Promise,使用了 GYP 构建系统,package.json 和 node_modules 的设计失 误等。


在演讲中 Ryan Dahl 宣布了一个新项目:Deno,一个新的服务端 Javascript 运行时。经过两年多的发展,Deno 已经发布了 1.8 版本,也有了一个活跃的开发者社区。我们今天就来简单的了解一下 Deno。

Deno 简介

Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.


Deno 使用 V8 引擎,由 Rust 构建,是一个简单、现代、安全的 Javascript 和 Typescript 运行时。


Deno 的主要特性有:


  • 默认安全。外部代码没有文件系统、网络、环境的访问权限,除非显式开启。

  • 支持开箱即用的 TypeScript 的环境。

  • 只分发一个独立的可执行文件 (deno)。

  • 有着内建的工具箱,比如一个依赖信息查看器 (deno info) 和一个代码格式化工具 (deno fmt)。

  • 有一组经过审计的标准模块,保证能在 Deno 上工作。

  • 脚本代码能被打包为一个单独的 JavaScript 文件。


安装 Deno

可以执行快速安装脚本方便地将 Deno 安装到不同操作系统上。


Mac 和 Linux 平台


curl -fsSL https://deno.land/x/install/install.sh | sh
复制代码


Windows 平台(使用 Powershell)


iwr https://deno.land/x/install/install.ps1 -useb | iex
复制代码

第一个 Deno 脚本

安装好 Deno 后,我们就可以直接在终端里使用 Deno 运行 Javascript/Typescript 脚本。


$ deno run https://deno.land/std/examples/welcome.tsDownload https://deno.land/std/examples/welcome.tsWarning Implicitly using latest version (0.91.0) for http://deno.land/std/examples/welcome.tsDownload https://deno.land/std@0.91.0/examples/welcome.tsCheck https://deno.land/std/examples/welcome.tsWelcome to Deno!
复制代码


有例子中我们可以知道deno run可以直接运行远程脚本,当然运行本地脚本也不是问题。


$ echo 'console.log("Hello, World!")' > hello.ts$ deno run .\hello.tsCheck file:///C:/Users/duyix/lab/deno-code/hello.tsHello, World!
复制代码

使用 Deno 运行 HTTP Server

我们再运行一下 Deno 官网示例中的启动 HTTP Server 的程序。


import { serve } from "https://deno.land/std@0.90.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 run .\server.tsDownload https://deno.land/std@0.90.0/http/server.ts# 省略了一些依赖下载的日志Check file:///C:/Users/duyix/lab/deno-code/server.tserror: Uncaught PermissionDenied: network access to "0.0.0.0:8000", run again with the --allow-net flag    at processResponse (core.js:223:11)    at Object.jsonOpSync (core.js:246:12)    at opListen (deno:cli/rt/30_net.js:32:17)    at Object.listen (deno:cli/rt/30_net.js:207:17)    at serve (server.ts:304:25)    at server.ts:2:11
复制代码


可以看到直接运行server.ts会失败,Deno 报错PermissionDenied。这是 Deno 默认的安全策略的行为,使用 Deno 运行脚本时默认不提供网络连接、文件 IO 等权限,需要显式的在deno run命令中启用对应的权限,在这个例子中我们需要加上--allow-net标志来启用网络权限。


$ deno run --allow-net .\server.tshttp://localhost:8000/
复制代码


然后我们就可以访问本地的 8000 端口了。


$ http :8000HTTP/1.1 200 OKcontent-length: 12
Hello World
复制代码

Deno REPL

和 Node 类似,Deno 也有 REPL 模式,直接运行deno repdeno就可以交互式地运行 JavaScript 脚本了,Deno 的 REPL 模式暂时还不提供 Typescript 支持。


$ denoDeno 1.5.4exit using ctrl+d or close()> let resp = await fetch('https://httpbin.org/headers')undefined> await resp.json(){  headers: {    Accept: "*/*",    "Accept-Encoding": "gzip, br",    Host: "httpbin.org",    "User-Agent": "Deno/1.5.4",    "X-Amzn-Trace-Id": "Root=1-6058b379-7fd616e829aa462029478558"  }}> close()
复制代码


我们在 Deno REPL 中使用了 fetch 进行网络请求,Deno 实现了一些常见的Web API。并且可以看到在 REPL 模式中,deno 默认开启了网络权限。

使用 Deno 编写命令行程序

Deno 是编写命令行工具的一个新的选择。


// dn-echo.tsconsole.log(Deno.args.join(" "));
复制代码


$ deno run ./dn-echo.ts 1 2 31 2 3
复制代码


Deno是 Deno 的一个全局对象,提供了一些系统 API(命令行参数获取、文件读写等)。可以通过Deno.arg获取命令行参数。


使用 Deno 编写命令行程序另一个便捷之处是我们可以很方便地通过deno install命令将本地脚本或网络脚本安装成全局工具。


$ deno install .\dn-echo.ts✅ Successfully installed dn-echoC:\Users\duyix\.deno\bin\dn-echo.cmd
复制代码


值得注意的是deno install命令也要加上对应的权限选项。


$ deno install -n dn-cat --allow-read https://deno.land/std@0.90.0/examples/cat.tsDownload https://deno.land/std@0.90.0/examples/cat.tsCheck https://deno.land/std@0.90.0/examples/cat.ts✅ Successfully installed dn-catC:\Users\duyix\.deno\bin\dn-cat.cmdC:\Users\duyix\.deno\bin\dn-cat (shell)
$ dn-cat .\hello.tsconsole.log("Hello, World!")
复制代码


我们从 url 安装了一个命令行脚本 cat(使用-n dn-cat选项指定了全局工具的名字),并且加上了--allow-read选项来开启读文件权限。

使用 Deno 开发 Web 应用

作为 Node.js 的挑战者,开发 Web 应用自然是 Deno 的看家本领。Deno 标准库http模块的功能已经很齐全了。不过为了更佳的开发体验,我们在这里使用oak框架(借鉴了 Node.js 中的 koa 框架)来开发 web 应用。


//app.jsimport { Application, Router } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
const port = 8000;
// Loggerapp.use(async (ctx, next) => { await next(); const rt = ctx.response.headers.get("X-Response-Time"); console.log(`HTTP ${ctx.request.method} on ${ctx.request.url}`); console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);});
// Timingapp.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; ctx.response.headers.set("X-Response-Time", `${ms}ms`);});
const router = new Router();
router .get("/", (ctx) => { ctx.response.body = "Hello Deno"; }) .get("/:name", (ctx) => { ctx.response.body = `Hello ${ctx.params.name}`; });
app.use(router.routes());app.use(router.allowedMethods());
app.addEventListener("listen", () => { console.log(`Listening on localhost:${port}`);});
await app.listen({ port });
复制代码


app.js中,我们初始化了一个Application对象app,注册了两个中间件(分别用来打日志和记录请求时间)。然后使用oak框架提供的Router组件实例化了一个router对象,定义了两个接口并将router注册到了app上。随后在app上注册了一个事件监听,在listen事件发生(也就是应用开始运行)时,输出了相关日志。


我们可以直接运行app.js


$ deno run --allow-net ./app.jsListening on localhost:8000
复制代码


随后就可以访问接口了。


$ http :8000/HTTP/1.1 200 OKcontent-length: 10content-type: text/plain; charset=utf-8x-response-time: 1ms
Hello Deno
$ http :8000/mikeHTTP/1.1 200 OKcontent-length: 10content-type: text/plain; charset=utf-8x-response-time: 0ms
Hello mike
复制代码

部署 Deno Web 应用

第一次运行app.js的时候 Deno 需要从互联网上下载其相关依赖, 我们可以使用deno cache命令手动将指定脚本的依赖缓存到本地(使用-r选项可以强制重新加载)。


$ deno cache -r ./app.js
复制代码


deno cache命令在构建镜像时还是很必要的,因为无法保证容器在每次运行的时候都可以顺利下载好依赖。直接在构建阶段将依赖准备好是一个更稳妥的选择。


一个常见的 Deno Web 应用的 Dockerfile 如下:


FROM hayd/alpine-deno:1.8.2
# The port that your application listens to.EXPOSE 1993
WORKDIR /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.ts
CMD ["run", "--allow-net", "main.ts"]
复制代码

打包脚本

Deno CLI 提供deno bundle命令可以将指定脚本及其依赖打包成一个 javascript 文件(类似 webpack),提供了另外一种分发应用的方式。


$ deno bundle ./app.js app.bundle.jsBundle file:///C:/Users/duyix/lab/deno-code/app.jsCheck file:///C:/Users/duyix/lab/deno-code/app.jsEmit "app.bundle.js" (402.67KB)
复制代码


可以看到打包生成的脚本还是很大的,不过根据我的观察app.bundle.js其实是格式化良好的,并且里面的函数名和参数名等都没有缩减过。deno bundle的输出文件大小精剪还是有很大优化空间的。


我个人还是更推荐使用容器镜像运行 Deno 应用,更规范,也更容易维护应用源代码和依赖。

总结

Deno 毕竟还是一个很年轻的项目,稳定性和配套的资源和支持与 Node.js 相比还是有很大差距的。但正如 Ryan Dahl 所说,Node.js 有其固有的一些历史遗留的设计问题 。Deno 的优秀理念正在吸引越来越多的开发者使用并加入社区,相信 Deno 会成为服务端 Web 开发领域的新秀。


原文

发布于: 2021 年 06 月 17 日阅读数: 9
用户头像

★忆先★

关注

还未添加个人签名 2018.11.12 加入

Python后端开发

评论

发布
暂无评论
初探Deno.js