Deno 入門手冊:附大量 TypeScript 代碼實例

  • 原文地址:在 freeCodeCamp 中文社區閱讀原文 The Deno Handbook
  • 原文做者:Flavio Copes
  • 原文發佈時間:2020-05-12
  • 譯者:hylerrix(韓亦樂), Yunkou(寇雲)
  • 校對者:hylerrix(韓亦樂)
  • 備註:本文遵循 freeCodeCamp 翻譯規範,同時本文會收錄在《Deno 鑽研之術》的翻譯篇中。
  • 備註:非營利組織 freeCodeCamp.org 自 2014 年成立以來,以「幫助人們免費學習編程」爲使命,建立了大量免費的編程教程,包括交互式課程、視頻課程、文章等。線下開發者社區遍及 160 多個國家、2000 多個城市。咱們正在幫助全球數百萬人學習編程,但願讓世界上每一個人都有機會得到免費的優質的編程教育資源,成爲開發者或者運用編程去解決問題。搜索關注微信公衆號 「freeCodeCamp」,可瞭解更多信息。

我每週都在探索新的項目,不多會有一個像 Deno 這樣的項目讓我如此着迷。html

在本手冊中我想要讓你快速入手 Deno。我會將其與 Node.js 進行對比,而後助力你在 Deno 上搭建第一個 REST API Demo。java

目錄

  1. 什麼是 Deno?
  2. 爲何是 Deno?爲何是如今?
  3. 你應該學習 Deno 嗎?
  4. Deno 將取代 Node.js 嗎?
  5. 一流的 TypeScript 支持
  6. 與 Node.js 的異同
  7. 再也不有包管理器
  8. 安裝 Deno
  9. Deno 命令
  10. 你的第一個 Deno 應用
  11. Deno 代碼實例
  12. 你的第一個 Deno 應用(深刻版)
  13. Deno 安全沙箱(Sandbox)
  14. 格式化代碼
  15. 標準庫
  16. 另外一個 Deno 示例
  17. Deno 是否有 Express/Hapi/Koa/*?
  18. 示例:使用 Oak 構建 REST API
  19. 更多內容
  20. 花絮
  21. 結語

此外,你能夠在此處獲取此 Deno 手冊的 PDF / ePub / Mobi 版本。node

什麼是 Deno?

若是你熟悉流行的服務器端 JavaScript 運行時 Node.js,那麼 Deno 就像 Node.js 同樣,但卻在不少方面都獲得了深入改善的全新 JavaScript / TypeScript 運行時。git

讓咱們從 Deno 的功能列表快速瞭解:程序員

  • Deno 基於最新的 JavaScript 語言;
  • Deno 具備覆蓋面普遍的標準庫;
  • Deno 以 TypeScript 爲核心,配以更多獨特的方式從而帶來了巨大的優點,其中包括一流的 TypeScript 支持(Deno 自動編譯 TypeScript 而無需你單獨編譯);
  • Deno 大力擁抱 ES 模塊標準;
  • Deno 沒有包管理器;
  • Deno 具備一流的 await 語法支持;
  • Deno 內置測試工具;
  • Deno 旨在儘量地與瀏覽器兼容,例如經過提供內置對象 fetch 和全局 window 對象。

咱們將在本手冊中展開探索全部上述功能。github

在你實戰完 Deno 並瞭解它獨特的功能魅力後,Node.js 或許會看起來有些過期。typescript

特別是由於 Node.js 的 API 是基於回調機制的,由於 Node.js 是在 Promise 和 Async / Await 定義在標準以前編寫的。Node.js 中沒法對此機制進行全新的更改,由於此類更改將產生「毀滅性」的影響。所以,在 Node.js 中咱們陷入了回調大量 API 的困境。shell

Node.js 的確很棒,並在可見的將來將繼續成爲 JavaScript 世界中事實上的標準。但我認爲咱們將逐漸看到 Deno 會因其一流的 TypeScript 支持和其內置的、覆蓋面普遍的現代標準庫而愈來愈被重視和採用。express

因爲沒有向後兼容性的歷史緣由,Deno 將能夠承擔起全部使用現代 Web 技術編寫的工程建設。但目前的現實是,咱們也沒法保證十年以內 Deno 不會發生像 Node.js 一樣的事情,而且不會出現一項新技術代替 Deno。編程

爲何是 Deno?爲何是如今?

大約 2 年前,Node.js 的建立者 Ryan Dahl 在 JSConf EU 上首次介紹了 Deno。觀看當時的演講視頻會很是有趣。若是你平時在大量接觸 Node.js 和 JavaScript,這個視頻請不要錯過。

每一個項目經理都必須下發決定。Ryan 回看 Node.js 中的一些早期設計依然感受十分遺憾。此外,在 ES6/2016/2017 等持續發展中的標準加持下,現在的 JavaScript 與 2009 年 Node.js 創立時的 JavaScript 已經大不相同。

所以,他開啓了一個全新項目,從而建立出服務器端的第二代 JavaScript 運行時。

新生的技術須要大量時間才能成熟,這正是我如今撰寫本手冊而不是兩年前就開始撰寫的緣由。現在,第一個正式穩定的 Deno v1.0 版本終於指日可待(不出意外的話,v1.0 會在 2020 年 5 月 13 日發佈)。

譯者注:翻譯本手冊時 Deno 1.0 已經發布。

1.0 看起來僅僅是個數字,但在社區約定下,意味着直到 Deno 2.0 前 Deno 都不會有太多重大的破壞性改變——這很重要,由於你終於能夠安心學習 Deno 當前的穩定版本了。

你應該學習 Deno 嗎?

這並不那麼容易回答。

學習像 Deno 這樣全新的知識須要很多的前期技術沉澱。個人建議是:若是你如今纔開始在服務器端使用 JavaScript 編程,而且你還不瞭解 Node.js,更沒有任何 TypeScript 應用開發經驗——那麼請從 Node.js 學起。

畢竟用通俗觀點來講,沒有人會在現在由於選擇學習 Node.js 而被解僱。

但若是你喜歡 TypeScript、也不想讓項目中依賴無比龐大的 NPM 軟件包、還想要隨時隨地使用 await 等語法,那麼你可能真的須要 Deno。

Deno 將取代 Node.js 嗎?

不能。Node.js 的生態已經十分龐大和完善,得到了數以萬計的優秀技術支持,將能再戰數十年。

一流的 TypeScript 支持

Deno 基於 Rust 和 TypeScript 這兩種今天正在迅速發展的語言編寫。

這意味着,即便咱們可能選擇編寫純 JavaScript 代碼來運行在基於 TypeScript 語言編寫的 Deno 上,咱們也能夠得到 TypeScript 的不少好處。

使用 Deno 運行 TypeScript 代碼無需任何手動編譯——Deno 會自動爲你執行此步驟。

你沒必要非得在 Deno 上編寫 TypeScript 代碼,可是 Deno 因其核心由 TypeScript 語言編寫的以下相關背景是不容忽視的:

首先,愈來愈多的 JavaScript 程序員開始喜歡上了 TypeScript 語言。

其次,你使用的工具能夠方便地推斷出許多有關用 TypeScript 語言編寫的軟件的信息,例如 Deno。

所以,當咱們在 VS Code(緊密集成 TypeScript 的編輯器)上的編碼環節就能及時地體會到類型檢查和高級智能感知(IntelliSense)功能帶來的好處。換句話說,編輯器能夠以很是有用的方式來幫助咱們瞭解 TypeScript 項目。

與 Node.js 的異同

因爲 Deno 從某種角度來說是 Node.js 的替代品,所以直接比較二者的異同對咱們的理解會頗有幫助。

類似之處:

  • 二者都是基於 V8 引擎開發的;
  • 二者都很是適合在服務器端上編寫 JavaScript 應用。

差別之處:

  • Node.js 用 C++ 和 JavaScript 語言編寫。Deno 用 Rust 和 TypeScript 語言編寫。
  • Node.js 有一個官方的軟件包管理器,稱爲 NPM。Deno 不會有,而會容許你從 URL 導入任何 ES 模塊。
  • Node.js 使用 CommonJS 模塊語法導入軟件包。Deno 使用 ES 標準模塊導入。
  • Deno 在其全部 API 和標準庫中都使用現代 ECMAScript 功能,而 Node.js 使用基於回調的標準庫,而且沒有計劃對其進行升級。
  • Deno 經過權限控制提供了一個安全的沙箱環境,程序只能訪問由用戶設置爲可執行標誌的文件。Node.js 程序能夠直接訪問用戶足以訪問的任何內容。
  • Deno 長期以來一直在探索將程序編譯成單個可執行文件的可能性,從而使得該可執行文件能夠在沒有外部依賴項(例如 Go)的狀況下運行,但這並不是一件容易的事,若是作獲得,將會成爲更有話語權的遊戲規則改變者。

沒有包依賴管理器

沒有像 NPM 同樣的程序包管理器而且大量依靠 URL 來承載和導入程序包是有利有弊的。但我真的很喜歡這個特性:它將會很是靈活,咱們能夠直接建立軟件包而無需在 NPM 這樣的存儲庫中發佈它們。

雖然尚未官方的消息,但我認爲 Deno 下的某種軟件包管理器將會出現。

與此同時,Deno 網站爲第三方軟件包提供代碼託管服務(並幫助其經過 URL 分發):詳見 https://deno.land/x/

安裝 Deno

就閒聊到這裏吧!讓咱們開始着手安裝 Deno。

最簡單的方法是使用 Homebrew

brew install deno

輸出如上命令後,你將能夠訪問 deno 命令。幫助是deno --help

譯者注:若是 HomeBrew 安裝太慢能夠嘗試輸入以下命令手動關閉 HomeBrew 的自動更新檢測: export HOMEBREW_NO_AUTO_UPDATE=true
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 命令

請注意上節中 deno --helpSUBCOMMANDS 中的部分,其中列出了咱們在當前版本(0.42.0)中能夠運行的全部命令,以下:

  • bundle :將項目的模塊和依賴項捆綁到單個文件中;
  • cache :緩存依賴項;
  • completions :generate shell completions;
  • doc :顯示某模塊的文檔;
  • eval :運行一段代碼,例如 deno eval "console.log(1 + 2) ;
  • fmt :內置的代碼格式化程序(相似於 Go 語言中的 gofmt);
  • help :打印某消息或某給定子命令的幫助信息;
  • info :顯示有關緩存的信息或與源文件有關的信息;
  • install :將腳本安裝爲可執行文件;
  • repl :開啓 REPL 環境(默認子命令);
  • run :運行給定文件名或 URL 的程序;
  • test :運行測試;
  • types :打印運行時的 TypeScript 聲明;
  • upgrade :升級 Deno 到最新版本。

你能夠運行 deno <subcommand> help 以獲取該子命令的特定文檔,例如 deno run --help

以下所示,咱們能夠直接輸入 deno 命令命令來默認啓動 REPL(Read-Execute-Print-Loop)環境直接調試功能,這與運行 deno repl 效果是相同的。

一個更常見的直接使用 deno 命令的場景是執行在 TypeScript 文件中寫的 Deno 應用程序。

譯者注:如今須要使用 deno run 命令而非 deno 命令來執行 TypeScript 文件。

你能夠同時運行 TypeScript(.ts)文件或 JavaScript(.js)文件。

若是你不熟悉 TypeScript,請不要擔憂——Deno 是用 TypeScript 編寫的,而且你可使用純 JavaScript 編寫「客戶端」應用程序。

若是你想快速上手的 TypeScript 話,能夠閱讀個人 TypeScript 教程

你的第一個 Deno 應用

讓咱們來運行第一個 Deno 應用程序。

Deno 讓我感到很是驚奇的特性是:你甚至沒必要寫一行代碼,即可以直接運行任何 URL 上的 Deno 應用程序。

此時 Deno 會將 URL 上的程序下載到本地並進行編譯,而後運行:

固然,我通常不建議從 Internet 運行沒法保障安全性的代碼。在這種狀況下,咱們先運行 Deno 官方網站上提供的 Demo;另外 Deno 還有一個沙箱,能夠阻止程序執行你不但願作的事情。稍後再詳細介紹。

這個程序很簡單,只須要一個console.log()調用:

console.log("Welcome to Deno 🦕");

若是使用瀏覽器打開直接打開 https://deno.land/std/examples/welcome.ts 這個 URL,則會看到如下頁面:

奇怪吧?你可能期待着打開 URL 後出現一個純 TypeScript 文件以供下載,可是咱們卻看到了一個網頁。緣由是 Deno 網站的 Web 服務器知道你正在使用瀏覽器,併爲你提供了對用戶更加友好的頁面。

爲了驗證這個功能,咱們可使用 wget 命令來測試這個 URL, wget 使用 text/plain 下載文本而不是  text/html

若是你想再運行這個程序,如今已經被 Deno 緩存了,不須要再下載和編譯了。

你能夠用 --reload 參數強制從新下載和編譯原始源碼。

在當前版本(0.42.0)中,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 代碼實例

除了前文咱們運行的 Demo 外,Deno 官網還提供了一些其餘的例子,能夠在這裏查看:https://deno.land/std/examples/

譯者注:你可能須要配置代理來更好地訪問 DenoLand。

在撰寫本手冊時,咱們能夠找到:

  • cat.ts :打印的內容是做爲參數提供的文件列表;
  • catj.ts :打印的內容是做爲參數提供的文件列表;
  • chat/ :聊天的一種實現;
  • colors.ts :打印一個彩色版本的 Hello world!;
  • 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](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 模塊中導入服務函數。可見咱們不須要先安裝這些模塊,並且也不會像 Node.js 那樣將這些模塊大量存儲在本地機器上。這也是 Deno 安裝速度快的緣由之一。

[https://deno.land/std/http/server.ts](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);
}

咱們接下來實例化一個服務器,調用 server() 函數傳遞一個帶有端口屬性的對象。

而後咱們運行以下循環來響應來自服務器的每個請求。

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

請注意,咱們在這裏使用 await 關鍵字而不須要將其封裝到異步函數中,由於 Deno 在其內部實現了頂層的 await 支持。

讓咱們在本地運行這個程序。假設你使用的是 VS Code(你可使用任何你喜歡的編輯器),我建議從 justjavac 開發的 Deno VS Code 擴展入手(當我嘗試的時候還有一個同名的擴展,可是已經被淘汰了,可能未來會消失)。

譯者注:justjavac 的 Deno VS Code 拓展將被官方收錄,之後能夠直接使用官方的拓展。

該擴展將爲 VS Code 提供幾個實用工具和不錯的東西來幫助你編寫應用程序。

如今在一個文件夾中建立一個 app.ts 文件,而後粘貼上面的代碼。

如今用 deno run app.ts 命令運行它。

Deno 會先下載、編譯咱們導入的那個依賴及其全部須要的依賴項。

這是因爲咱們導入的 [https://deno.land/std/http/server.ts](https://deno.land/std/http/server.ts) 文件自己就有數個其它依賴:

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 的 Sandbox 問題,咱們一塊兒來看看。

Deno 安全沙箱(Sandbox)

我以前提到過,Deno 有一個安全沙箱,能夠防止程序作一些你不容許的事情。

這意味着什麼呢?

Ryan 曾在 Deno 的介紹講座中提到的一件事是:有時候你想在 Web 瀏覽器以外運行一個 JavaScript 程序,卻不想讓它肆意在你的系統中訪問任何它想要的東西,好比使用網絡與外部世界對話。

爲何咱們一般只安裝來自可信來源的 Node.js 包?這是由於沒有什麼能夠阻止 Node.js 程序獲取你係統上的 SSH 密鑰或其餘任何東西,並將其發送到服務器上。可是,咱們該怎麼知道本身或其餘人使用的一個項目是否被黑客入侵了?

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相同)。

其中,netreadwrite 的權限能夠是細化的。例如,你可使用 --allow-read=/dev,容許從特定文件夾中讀取。

格式化代碼

Go 語言編譯器自帶的 gofmt 命令是我很是喜歡 Go 語言特性之一。全部的 Go 代碼的格式看起來都是同樣的。每位 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 APP 的例子,以以下 cat.ts 爲例。

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.args 是一個包含全部發送到命令中的參數的變量。

咱們對這些參數進行迭代:對每個參數,咱們使用 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

我想在這裏作一個簡單的 Demo 實戰,介紹一下如何使用 Oak 框架構建REST API。Oak 頗有意思,由於它的靈感來自於 Koa,一個流行的 Node.js 中間件。正由於如此,若是你之前用過 Koa 的話,會很快熟悉 Oak。

咱們要構建的 API 示例也很是簡單。

咱們的服務器將在內存中存儲一個帶有名字和年齡的旺柴的列表。

咱們的需求是:

  • 添加旺柴;
  • 列出旺柴;
  • 獲取有關特定旺柴的詳細信息;
  • 從名單上刪除一隻旺柴;
  • 更新旺柴的年齡。

咱們將使用 TypeScript 進行此操做,可是沒有什麼能夠阻止你使用 JavaScript 編寫 API——你只須要刪除下方 TypeScript 文件中全部有關類型描述的代碼並將文件名後綴改成 .js

建立一個 app.ts 文件。

讓咱們開始從 Oak 導入 ApplicationRouter 對象:

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

而後咱們獲得環境變量 PORTHOST:

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 數組 Dog 對象。

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);

看到了嗎?咱們的 API 定義是:

  • 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 官方網站爲 https://deno.land

API 文檔位於 https://doc.deno.landhttps://deno.land/typedoc/index.html 中。

一份 Awesome Deno 資源清單 https://github.com/denolib/awesome-deno

譯者注:中文的 Awesome Deno 清單由譯者持續維護中,能夠訪問這裏: Awesome Deno 資源全圖譜

花絮

  • Deno 提供了一個內置的 fetch 實現,該實現與瀏覽器中可用的匹配。
  • Deno 正在進行與 Node.js stdlib 的兼容層

結語

 
我但願你喜歡這個 Deno 入門手冊!

別忘了,你能夠在此處獲取此 Deno 手冊的 PDF / ePub / Mobi 版本。

相關文章
相關標籤/搜索