Deno手册:对Deno的详细介绍

262 阅读18分钟

我每周都会探索新的项目,很少有像Deno这样引起我注意的项目。

什么是Deno?

如果你熟悉Node.js,流行的服务器端JavaScript生态系统,那么Deno就像Node。除了在许多方面有深刻的改进。

让我们从快速列出我最喜欢Deno的功能开始。

  • 它是基于JavaScript语言的现代特性的
  • 它有一个广泛的标准库
  • 它的核心是TypeScript,这在很多方面都带来了巨大的优势,包括一流的TypeScript支持(你不需要单独编译TypeScript,Deno会自动完成)。
  • 它拥抱ES模块
  • 它没有包管理器
  • 它有一个一流的await
  • 它作为一个内置的测试工具
  • 它的目标是尽可能地与浏览器兼容,例如通过提供一个内置的fetch 和全局window 对象。

我们将在本指南中探讨所有这些功能。

在你使用Deno并学会欣赏它的特性后,Node.js将看起来像个老东西

特别是因为Node.js的API是基于回调的,因为它是在承诺和async/await之前写的。在Node中没有任何改变,因为这样的改变将是巨大的,所以我们只能使用回调或承诺API调用。

Node.js非常,并将继续成为JavaScript世界中事实上的标准。但我认为我们会逐渐看到Deno被更多人采用,因为它有一流的TypeScript支持和现代标准库。

Deno可以负担得起所有用现代技术编写的东西,因为没有后向兼容性需要维护。当然,不能保证十年后Deno也会发生同样的情况,会出现新的技术,但这就是目前的现实。

为什么是Deno?为什么是现在?

Deno几乎是2年前由Node.js的原创者Ryan Dahl在JSConf EU宣布的。观看YouTube上的演讲视频,它非常有趣,如果你参与Node.js和一般的JavaScript,它是必须要看的。

每个项目经理都必须做出决定。Ryan对Node的一些早期决定感到后悔。另外,技术也在发展,今天的JavaScript与2009年Node开始时的情况完全不同。想想现代ES6/2016/2017的功能,等等。

所以他开始了一个新的项目,以创建某种第二波的JavaScript驱动的服务器端应用程序。

我之所以现在而不是当年写这篇指南,是因为技术需要大量时间来成熟。而我们终于达到了Deno 1.0(1.0应该在2020年5月13日发布),Deno的第一个版本正式宣布稳定。

这似乎只是一个数字,但1.0意味着在Deno 2.0之前不会有重大的突破性变化,当你潜心研究一项新技术时,这是一个大问题--你不希望学习一些东西,然后让它变化太快。

你应该学习Deno吗?

这是个大问题。

学习像Deno这样的新东西是一个很大的努力。我的建议是,如果你现在开始学习服务器端JS,而你还不知道Node,也从未写过任何TypeScript,我会从Node开始。

从来没有人因为选择Node.js而被解雇(套用一句常见的话)。

但是,如果你喜欢TypeScript,在你的项目中不依赖大量的npm包,而且你想在任何地方使用await ,嘿,Deno可能是你要找的东西。

它能取代Node.js吗?

不会。Node.js是一个巨大的、成熟的、令人难以置信的、得到良好支持的技术,它将保持几十年。

一流的TypeScript支持

Deno是用Rust和TypeScript编写的,这两种语言今天确实在快速发展。

特别是用TypeScript编写意味着我们可以得到TypeScript的很多好处,即使我们可能选择用普通的JavaScript编写代码。

而且用Deno运行TypeScript代码不需要编译步骤--Deno会自动为你编译。

你并不是被迫用TypeScript编写,但Deno的核心是用TypeScript编写的,这一点非常重要。

首先,越来越大比例的JavaScript程序员喜欢TypeScript。

其次,你使用的工具可以推断出许多用TypeScript编写的软件的信息,比如Deno。

这意味着,当我们在VS Code中编码时,例如,它显然与TypeScript紧密结合,因为两者都是在MicroSoft开发的,我们可以在写代码时获得类型检查等好处,以及高级IntelliSense功能。换句话说,编辑器可以以一种非常有用的方式帮助我们。

与Node.js的相似和不同之处

由于Deno基本上是一个Node.js的替代品,所以直接比较两者是很有用的。

相似性。

  • 两者都是基于V8 Chromium引擎开发的
  • 两者都很适合用JavaScript开发服务器端

不同之处。

  • Node是用C++和JavaScript编写的。Deno是用Rust和TypeScript编写的。
  • Node有一个官方的软件包管理器,叫做npm 。Deno没有,而是让你从URL导入任何ES模块。
  • Node使用CommonJS的语法来导入包管理器。Deno使用ES模块,是官方的方式。
  • Deno在其所有的API和标准库中使用现代ECMAScript特性,而Node.js使用基于回调的标准库,并且没有计划升级它。
  • Deno通过权限提供了一个沙盒安全层。一个程序只能访问由用户设置为旗帜的可执行文件的权限。一个Node.js程序可以访问用户可以访问的任何东西
  • 长期以来,Deno一直设想将一个程序编译成一个可执行文件,你可以在没有外部依赖的情况下运行,就像Go一样,但它仍然不是一个东西。这将是一个游戏规则的改变。

没有软件包管理器

没有软件包管理器,必须依靠URL来托管和导入软件包,这有利有弊。我非常喜欢它的优点:它非常灵活,我们可以创建包,而不需要在像npm这样的存储库中发布它们。

我认为某种包管理器会出现,但目前还没有正式的东西出来。

Deno网站为第三方软件包提供代码托管(从而通过URL发布):https://deno.land/x/

安装Deno

说得够多了!让我们来安装Deno。

最简单的方法是使用Homebrew

一旦这样做了,你就可以访问deno 命令了。下面是你使用deno --help ,可以得到的帮助。

flavio@mbp~> deno --help
deno 0.42.0
A secure JavaScript and TypeScript runtime

Docs: https://deno.land/std/manual.md
Modules: https://deno.land/std/ https://deno.land/x/
Bugs: https://github.com/denoland/deno/issues

To start the REPL, supply no arguments:
  deno

To execute a script:
  deno run https://deno.land/std/examples/welcome.ts
  deno https://deno.land/std/examples/welcome.ts

To evaluate code in the shell:
  deno eval "console.log(30933 + 404)"

Run 'deno help run' for 'run'-specific flags.

USAGE:
    deno [OPTIONS] [SUBCOMMAND]

OPTIONS:
    -h, --help
            Prints help information

    -L, --log-level <log-level>
            Set log level [possible values: debug, info]

    -q, --quiet
            Suppress diagnostic output
            By default, subcommands print human-readable diagnostic messages to stderr.
            If the flag is set, restrict these messages to errors.
    -V, --version
            Prints version information


SUBCOMMANDS:
    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 newest version

ENVIRONMENT VARIABLES:
    DENO_DIR             Set deno's base directory (defaults to $HOME/.deno)
    DENO_INSTALL_ROOT    Set deno install's output directory
                         (defaults to $HOME/.deno/bin)
    NO_COLOR             Set to disable color
    HTTP_PROXY           Proxy address for HTTP requests
                         (module downloads, fetch)
    HTTPS_PROXY          Same but for HTTPS

Deno命令

注意帮助中的SUBCOMMANDS 部分,它列出了我们可以运行的所有命令。我们有哪些子命令?

  • bundle 将一个项目的模块和依赖关系捆绑到一个文件中
  • cache 缓存依赖项
  • completions 生成shell完成度
  • doc 显示一个模块的文档
  • eval 评估一段代码,比如说。deno eval "console.log(1 + 2)"
  • fmt 一个内置的代码格式化器(类似于Go中的 )。gofmt
  • help 打印这个信息或给定的子命令的帮助
  • info 显示关于缓存的信息或与源文件有关的信息
  • install 将脚本安装为可执行文件
  • repl 读取-评估-打印-循环(默认)。
  • run 运行一个给定文件名或模块网址的程序
  • test 运行测试
  • types 打印运行时TypeScript的声明
  • upgrade 升级 到最新的版本deno

你可以运行deno <subcommand> help ,以获得该命令的特定附加文档,例如deno run --help

正如帮助中所说,我们可以使用这个命令来启动一个REPL(Read-Execute-Print-Loop),使用deno ,而不需要任何其他选项。

这与运行deno repl 是一样的。

你使用这个命令的一个更常见的方式是执行一个包含在TypeScript文件中的Deno应用。

你可以运行TypeScript (.ts) 文件,或者JavaScript (.js) 文件。

如果你不熟悉TypeScript,不用担心:Deno是用TypeScript编写的,但你可以用JavaScript编写你的 "客户端 "应用程序。

如果你愿意的话,我的TypeScript教程将帮助你快速掌握TypeScript。

你的第一个Deno应用程序

让我们第一次运行一个Deno应用程序。

我觉得相当神奇的是,你甚至不需要写一行字--你可以从任何URL运行一个命令。

Deno下载程序,编译它,然后运行它。

当然,从互联网上运行任意代码的做法一般不值得推荐。在这种情况下,我们是从Deno的官方网站上运行它,另外Deno有一个沙盒,可以防止程序做任何你不想允许的事情。稍后会有更多这方面的内容。

这个程序非常简单,只是一个console.log() 的调用。

console.log('Welcome to Deno 🦕')

如果你用浏览器打开deno.land/std/example…URL,你会看到这个页面。

很奇怪,对吗?你可能会期待一个TypeScript文件,而我们却有一个网页。原因是Deno网站的Web服务器知道你在使用浏览器,并为你提供一个更友好的页面。

例如,使用wget 下载同一个UR,它请求的是text/plain ,而不是text/html

如果你想再次运行这个程序,它现在已经被Deno缓存了,它不需要再次下载它。

你可以用--reload 标志强制重新加载原始源代码。

deno run 有很多不同的选项,在 中没有列出。相反,你需要运行 来显示它们。deno --help deno run --help

flavio@mbp~> deno run --help
deno-run
Run a program given a filename or url to the module.

By default all programs are run in sandbox without access to disk, network or
ability to spawn subprocesses.
  deno run https://deno.land/std/examples/welcome.ts

Grant all permissions:
  deno run -A https://deno.land/std/http/file_server.ts

Grant permission to read from disk and listen to network:
  deno run --allow-read --allow-net https://deno.land/std/http/file_server.ts

Grant permission to read whitelisted files from disk:
  deno run --allow-read=/etc https://deno.land/std/http/file_server.ts

USAGE:
    deno run [OPTIONS] <SCRIPT_ARG>...

OPTIONS:
    -A, --allow-all
            Allow all permissions

        --allow-env
            Allow environment access

        --allow-hrtime
            Allow high resolution time measurement

        --allow-net=<allow-net>
            Allow network access

        --allow-plugin
            Allow loading plugins

        --allow-read=<allow-read>
            Allow file system read access

        --allow-run
            Allow running subprocesses

        --allow-write=<allow-write>
            Allow file system write access

        --cached-only
            Require that remote dependencies are already cached

        --cert <FILE>
            Load certificate authority from PEM encoded file

    -c, --config <FILE>
            Load tsconfig.json configuration file

    -h, --help
            Prints help information

        --importmap <FILE>
            UNSTABLE:
            Load import map file
            Docs: https://deno.land/std/manual.md#import-maps
            Specification: https://wicg.github.io/import-maps/
            Examples: https://github.com/WICG/import-maps#the-import-map
        --inspect=<HOST:PORT>
            activate inspector on host:port (default: 127.0.0.1:9229)

        --inspect-brk=<HOST:PORT>
            activate inspector on host:port and break at start of user script

        --lock <FILE>
            Check the specified lock file

        --lock-write
            Write lock file. Use with --lock.

    -L, --log-level <log-level>
            Set log level [possible values: debug, info]

        --no-remote
            Do not resolve remote modules

    -q, --quiet
            Suppress diagnostic output
            By default, subcommands print human-readable diagnostic messages to stderr.
            If the flag is set, restrict these messages to errors.
    -r, --reload=<CACHE_BLACKLIST>
            Reload source code cache (recompile TypeScript)
            --reload
              Reload everything
            --reload=https://deno.land/std
              Reload only standard modules
            --reload=https://deno.land/std/fs/utils.ts,https://deno.land/std/fmt/colors.ts
              Reloads specific modules
        --seed <NUMBER>
            Seed Math.random()

        --unstable
            Enable unstable APIs

        --v8-flags=<v8-flags>
            Set V8 command line options. For help: --v8-flags=--help


ARGS:
    <SCRIPT_ARG>...
            script args

Deno代码示例

除了我们上面运行的那个,Deno网站还提供了一些其他的例子,你可以去看看:https://deno.land/std/examples/。

在写这篇文章的时候,我们可以发现。

  • cat.ts 打印作为参数提供的文件列表中的内容
  • catj.ts 打印内容和作为参数提供的文件列表
  • chat/ 一个聊天的实现
  • colors.ts 一个例子
  • curl.ts curl 的一个简单实现,打印作为参数指定的URL的内容
  • echo_server.ts 一个TCP回声服务器
  • gist.ts 一个向gist.github.com发布文件的程序
  • test.ts 一个测试套件的样本
  • welcome.ts 一个简单的console.log语句(上面我们运行的第一个程序)。
  • xeval.ts 允许你对收到的任何一行标准输入运行任何TypeScript代码。曾经被称为deno xeval,但后来从官方命令中删除。

你的第一个Deno应用程序(真实的)

让我们来写一些代码。

你使用deno run https://deno.land/std/examples/welcome.ts 运行的第一个Deno应用是别人写的应用,所以你没有看到任何关于Deno代码的样子。

我们将从Deno官方网站上列出的默认示例应用程序开始。

import { serve } from 'https://deno.land/std/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' })
}

这段代码从http/server 模块中导入了serve 函数。看到了吗?我们不需要先安装它,而且它也不像Node模块那样存储在你的本地机器上。这就是为什么Deno的安装如此之快的原因之一。

https://deno.land/std/http/server.ts ,导入的是模块的最新版本。你可以使用@VERSION ,像这样导入一个特定的版本。

import { serve } from 'https://deno.land/std@v0.42.0/http/server.ts'

在这个文件中,serve 函数是这样定义的。

/**
 * Create a HTTP server
 *
 *     import { serve } from "https://deno.land/std/http/server.ts";
 *     const body = "Hello World\n";
 *     const s = serve({ port: 8000 });
 *     for await (const req of s) {
 *       req.respond({ body });
 *     }
 */
export function serve(addr: string | HTTPOptions): Server {
  if (typeof addr === 'string') {
    const [hostname, port] = addr.split(':')
    addr = { hostname, port: Number(port) }
  }

  const listener = listen(addr)
  return new Server(listener)
}

我们继续实例化一个服务器,调用serve() 函数,传递一个带有port 属性的对象。

然后我们运行这个循环来响应来自服务器的每个请求。

for await (const req of s) {
  req.respond({ body: 'Hello World\n' })
}

注意,我们使用了await 关键字,而不需要把它包装成一个async 函数,因为Deno实现了顶层的await

让我们在本地运行这个程序。我假设你使用VS Code,但你可以使用任何你喜欢的编辑器。

我建议安装来自justjavac 的Deno扩展(在我尝试的时候还有一个同名的扩展,但是已经废弃了--将来可能会消失)。

该扩展将为VS Code提供一些实用工具和好东西,以帮助你编写你的应用程序。

现在在一个文件夹中创建一个app.ts 文件并粘贴上述代码。

现在用deno run app.ts 来运行它。

Deno会下载它所需要的所有依赖项,首先下载我们导入的那个文件。

deno.land/std/http/se…文件本身就有几个依赖项。

import { encode } from '../encoding/utf8.ts'
import { BufReader, BufWriter } from '../io/bufio.ts'
import { assert } from '../testing/asserts.ts'
import { deferred, Deferred, MuxAsyncIterator } from '../async/mod.ts'
import {
  bodyReader,
  chunkedBodyReader,
  emptyReader,
  writeResponse,
  readRequest,
} from './_io.ts'
import Listener = Deno.Listener
import Conn = Deno.Conn
import Reader = Deno.Reader

而这些都是自动导入的。

但在最后,我们有一个问题。

发生了什么?我们有一个权限拒绝的问题。

让我们来谈谈沙盒。

Deno沙盒

我之前提到,Deno有一个沙盒,可以防止程序做任何你不想允许的事情。

这意味着什么呢?

Ryan在Deno介绍的谈话中提到的一件事是,有时你想在Web浏览器之外运行一个JavaScript程序,但又不允许它在你的系统上访问任何它想访问的东西。或者使用网络与外部世界交谈。

没有什么能阻止一个Node.js应用程序获取你的SSH密钥或你系统上的任何其他东西并将其发送到服务器上。这就是为什么我们通常只从受信任的来源安装Node包,但如果我们使用的某个项目被黑了,反过来其他人也被黑了,我们怎么能知道?

Deno试图复制与浏览器实现的相同的权限模型。除非你明确允许,否则在浏览器中运行的任何JavaScript都不能在你的系统中做不正当的事情。

回到Deno,如果一个程序想要访问网络,就像前面的例子一样,那么我们需要给它许可。

我们可以通过在运行命令时传递一个标志来做到这一点,在这种情况下,--allow-net

deno run --allow-net app.ts

该程序现在正在8000端口上运行一个HTTP服务器。

其他标志允许Deno解锁其他功能。

  • --allow-env 允许环境访问
  • --allow-hrtime 允许高分辨率的时间测量
  • --allow-net=<allow-net> 允许网络访问
  • --allow-plugin 允许加载插件
  • --allow-read=<allow-read> 允许文件系统读取访问
  • --allow-run 允许运行子进程
  • --allow-write=<allow-write> 允许文件系统写访问
  • --allow-all 允许所有权限(与 相同)-A

net,readwrite 的权限可以是细化的。例如,你可以允许从一个特定的文件夹中阅读,使用--allow-read=/dev

格式化代码

我非常喜欢Go的其中一点是Go编译器附带的gofmt 命令。所有的Go代码看起来都一样。每个人都使用gofmt

JavaScript程序员习惯于运行Prettier,而deno fmt 实际上是在幕后运行的。

假设你有一个格式不好的文件,像这样。

你运行deno fmt app.ts ,它就会自动格式化,同时在缺少的地方加上分号。

标准库

尽管这个项目还很年轻,但Deno的标准库非常广泛。

它包括。

  • archive tar归档实用程序
  • async 异步实用程序
  • bytes 处理字节片的辅助工具
  • datetime 日期/时间解析
  • encoding 对各种格式进行编码/解码
  • flags 解析命令行标志
  • fmt 格式化和打印
  • fs 文件系统API
  • hash 密码库
  • http HTTP服务器
  • io I/O库
  • log 日志实用程序
  • mime 对多部分数据的支持
  • node Node.js兼容层
  • path 路径操作
  • ws websockets

另一个Deno的例子

让我们看看另一个Deno应用程序的例子,来自Deno的例子。 cat:

const filenames = Deno.args
for (const filename of filenames) {
  const file = await Deno.open(filename)
  await Deno.copy(file, Deno.stdout)
  file.close()
}

这就把Deno.args 的内容分配给了filenames 变量,这个变量包含了发给命令的所有参数。

我们遍历它们,对于每个参数,我们使用Deno.open() 来打开文件,我们使用Deno.copy() 来打印文件的内容到Deno.stdout 。最后我们关闭该文件。

如果你用以下方式运行这个

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

程序被下载和编译,但由于我们没有指定任何参数,所以什么也没有发生。

现在试试

deno run https://deno.land/std/examples/cat.ts app.ts

假设你在同一个文件夹里有前一个项目的app.ts

你会得到一个权限错误。

因为Deno默认不允许对文件系统的访问。使用--allow-read=./ ,授予对当前文件夹的访问权。

deno run --allow-read=./ https://deno.land/std/examples/cat.ts app.ts

Deno有一个Express/Hapi/Koa/*吗?

是的,肯定有。请看像这样的项目

例子:使用Oak建立一个REST API

我想做一个简单的例子,说明如何使用Oak构建一个REST API。Oak很有趣,因为它的灵感来自于Koa,即流行的Node.js中间件,由于这个原因,如果你以前使用过,就会觉得非常熟悉。

我们要建立的API非常简单。

我们的服务器将在内存中存储一个带有姓名和年龄的狗的列表。

我们想。

  • 增加新的狗
  • 列出狗
  • 获得关于某只狗的详细信息
  • 从列表中删除一只狗
  • 更新一只狗的年龄

我们将在TypeScript中做到这一点,但没有什么能阻止你在JavaScript中编写API--你只需删除类型。

创建一个app.ts 文件。

让我们先从Oak中导入ApplicationRouter 对象。

import { Application, Router } from 'https://deno.land/x/oak/mod.ts'

然后我们得到环境变量PORT和HOST。

const env = Deno.env.toObject()
const PORT = env.PORT || 4000
const HOST = env.HOST || '127.0.0.1'

默认情况下,我们的应用程序将在localhost:4000上运行。

现在我们创建Oak应用程序并启动它。

const router = new Router()

const app = new Application()

app.use(router.routes())
app.use(router.allowedMethods())

console.log(`Listening on port ${PORT}...`)

await app.listen(`${HOST}:${PORT}`)

现在,应用程序的编译工作应该很顺利。

运行

deno run --allow-env --allow-net app.ts

Deno将下载依赖项。

然后在4000端口上监听。

接下来你运行这个命令时,Deno会跳过安装部分,因为这些包已经被缓存了。

在文件的顶部,让我们定义一个狗的接口,然后我们声明一个狗对象的初始dogs 阵列。

interface Dog {
  name: string
  age: number
}

let dogs: Array<Dog> = [
  {
    name: 'Roger',
    age: 8,
  },
  {
    name: 'Syd',
    age: 7,
  },
]

现在让我们真正实现API。

我们已经准备好了一切。在你创建了路由器之后,让我们添加一些函数,当这些端点之一被击中时,这些函数将被调用。

const router = new Router()

router
  .get('/dogs', getDogs)
  .get('/dogs/:name', getDog)
  .post('/dogs', addDog)
  .put('/dogs/:name', updateDog)
  .delete('/dogs/:name', removeDog)

看到了吗?我们定义

  • GET /dogs
  • GET /dogs/:name
  • POST /dogs
  • PUT /dogs/:name
  • DELETE /dogs/:name

让我们逐一实现这些功能。

GET /dogs 开始,它返回所有狗的列表。

export const getDogs = ({ response }: { response: any }) => {
  response.body = dogs
}

接下来,我们可以通过名字来检索一只狗。

export const getDog = ({
  params,
  response,
}: {
  params: {
    name: string
  }
  response: any
}) => {
  const dog = dogs.filter((dog) => dog.name === params.name)
  if (dog.length) {
    response.status = 200
    response.body = dog[0]
    return
  }

  response.status = 400
  response.body = { msg: `Cannot find dog ${params.name}` }
}

下面是我们如何添加一只新的狗。

export const addDog = async ({
  request,
  response,
}: {
  request: any
  response: any
}) => {
  const body = await request.body()
  const dog: Dog = body.value
  dogs.push(dog)

  response.body = { msg: 'OK' }
  response.status = 200
}

注意,我现在用const body = await request.body() 来获取正文的内容,因为nameage 的值是以JSON形式传递的。

这里是我们如何更新一只狗的年龄。

export const updateDog = async ({
  params,
  request,
  response,
}: {
  params: {
    name: string
  }
  request: any
  response: any
}) => {
  const temp = dogs.filter((existingDog) => existingDog.name === params.name)
  const body = await request.body()
  const { age }: { age: number } = body.value

  if (temp.length) {
    temp[0].age = age
    response.status = 200
    response.body = { msg: 'OK' }
    return
  }

  response.status = 400
  response.body = { msg: `Cannot find dog ${params.name}` }
}

以及这里是我们如何从我们的列表中删除一只狗。

export const removeDog = ({
  params,
  response,
}: {
  params: {
    name: string
  }
  response: any
}) => {
  const lengthBefore = dogs.length
  dogs = dogs.filter((dog) => dog.name !== params.name)

  if (dogs.length === lengthBefore) {
    response.status = 400
    response.body = { msg: `Cannot find dog ${params.name}` }
    return
  }

  response.body = { msg: 'OK' }
  response.status = 200
}

这里是完整的示例代码。

import { Application, Router } from 'https://deno.land/x/oak/mod.ts'

const env = Deno.env.toObject()
const PORT = env.PORT || 4000
const HOST = env.HOST || '127.0.0.1'

interface Dog {
  name: string
  age: number
}

let dogs: Array<Dog> = [
  {
    name: 'Roger',
    age: 8,
  },
  {
    name: 'Syd',
    age: 7,
  },
]

export const getDogs = ({ response }: { response: any }) => {
  response.body = dogs
}

export const getDog = ({
  params,
  response,
}: {
  params: {
    name: string
  }
  response: any
}) => {
  const dog = dogs.filter((dog) => dog.name === params.name)
  if (dog.length) {
    response.status = 200
    response.body = dog[0]
    return
  }

  response.status = 400
  response.body = { msg: `Cannot find dog ${params.name}` }
}

export const addDog = async ({
  request,
  response,
}: {
  request: any
  response: any
}) => {
  const body = await request.body()
  const { name, age }: { name: string; age: number } = body.value
  dogs.push({
    name: name,
    age: age,
  })

  response.body = { msg: 'OK' }
  response.status = 200
}

export const updateDog = async ({
  params,
  request,
  response,
}: {
  params: {
    name: string
  }
  request: any
  response: any
}) => {
  const temp = dogs.filter((existingDog) => existingDog.name === params.name)
  const body = await request.body()
  const { age }: { age: number } = body.value

  if (temp.length) {
    temp[0].age = age
    response.status = 200
    response.body = { msg: 'OK' }
    return
  }

  response.status = 400
  response.body = { msg: `Cannot find dog ${params.name}` }
}

export const removeDog = ({
  params,
  response,
}: {
  params: {
    name: string
  }
  response: any
}) => {
  const lengthBefore = dogs.length
  dogs = dogs.filter((dog) => dog.name !== params.name)

  if (dogs.length === lengthBefore) {
    response.status = 400
    response.body = { msg: `Cannot find dog ${params.name}` }
    return
  }

  response.body = { msg: 'OK' }
  response.status = 200
}

const router = new Router()
router
  .get('/dogs', getDogs)
  .get('/dogs/:name', getDog)
  .post('/dogs', addDog)
  .put('/dogs/:name', updateDog)
  .delete('/dogs/:name', removeDog)

const app = new Application()

app.use(router.routes())
app.use(router.allowedMethods())

console.log(`Listening on port ${PORT}...`)

await app.listen(`${HOST}:${PORT}`)

了解更多

Deno的官方网站是deno.land

API文档可在doc.deno.landdeno.land/typedoc/ind…

awesome-denogithub.com/denolib/awe…

还有一些随机的小插曲

  • Deno提供了一个内置的fetch 实现,与浏览器中的实现相匹配。
  • Deno有一个与Node.js stdlib的兼容层,正在进行中