#Deno手冊:Deno的簡潔介紹 🦕

快速了解Deno,這是一個現代的Node.js替代方案

我每週都會探索新的專案,而且很少有像Deno這樣引起我的注意。

Deno的標誌

Deno是什麼?

如果你熟悉Node.js,那麼Deno就像Node一樣。但在很多方面上有很大的改進。

讓我們從一個我最喜歡Deno的快速功能列表開始:

  • 它是基於JavaScript語言的現代特性
  • 它擁有豐富的標準庫
  • 它的核心是TypeScript,這在很多方面都有很大的優勢,包括一流的TypeScript支持(您不需要單獨編譯TypeScript,Deno會自動處理)
  • 它採用ES模塊
  • 它沒有包管理器
  • 它有一流的等待機制
  • 它有內置的測試工具
  • 它的目標是盡可能與瀏覽器兼容,例如提供了內置的fetch和全局的window對象。

在本指南中,我們將探索所有這些功能。

在您使用Deno並學會欣賞其功能之後,Node.js將會變得像一個“老古董”。

特別是因為Node.js的API是基於回調的,因為它是在Promises和async/await之前開發的。目前在Node中沒有這方面的改變,因為這樣的改變將是巨大的,所以我們只能使用回調或將API調用轉換為Promises形式。

Node.js是驚人的,並且將繼續是JavaScript世界中的事實標準。但我認為由於其對TypeScript的一流支持和現代的標準庫,我們會逐漸看到Deno更多地被採用。

Deno能夠采用現代技術編寫所有代碼,因為它不需要維護向後兼容性。當然,在十年後,無法保證Deno仍然如此,可能會出現一種新技術,但這正是目前的現實情況。

為什麼選擇Deno?為什麼現在?

Deno在將近兩年前由Node.js的原始創作者Ryan Dahl在JSConf EU上宣布。觀看演講的YouTube視頻,它非常有趣,如果你從事Node.js和JavaScript開發,這是一個必看的。

每個項目經理都需要做出決策。Ryan後悔Node的一些早期決策。此外,技術是在不斷發展的,今天的JavaScript與2009年Node開始時完全不同。想想現代的ES6/2016/2017功能等等。

所以他開始了一個新項目,創建了第二波JavaScript驅動的服務器端應用程序。

我現在寫這篇指南,而不是之前,是因為技術需要很長時間成熟。我們終於到了Deno 1.0(Denno 1.0應在2020年5月13日發布),Deno的第一個官方穩定版本。

這只是一個數字,但1.0意味著在Deno 2.0之前不會有重大的破壞性變化,在開始學習一個新技術時這非常重要- 您不希望學習某些東西然後它就過時了。

您應該學習Deno嗎?

這是一個很大的問題。

學習與Deno這樣的新東西是一個很大的努力。我的建議是,如果您現在開始使用服務器端JavaScript,並且對Node還不熟悉,也從來沒有寫過任何TypeScript,那麼我會從Node開始。

沒有人因為選擇Node.js而失業(引用一個常見的說法)。

但是,如果你喜歡TypeScript,在項目中不依賴於大量的npm包,並且想要隨處使用await,那麼Deno可能是你在找的東西。

它能取代Node.js嗎?

不。Node.js是一個巨大的、很好支持的技術,在未來幾十年都會存在。

一流的TypeScript支持

Deno使用Rust和TypeScript編寫,這兩種語言在目前都有很大的增長。

特別是使用TypeScript編寫意味著,即使我們可能選擇使用純JavaScript編寫我們的代碼,但我們仍然能夠從TypeScript中獲得很多好處。

而使用Deno運行TypeScript代碼不需要編譯步驟- Deno會自動為您完成。

您不需要寫TypeScript,但Deno的核心部分是使用TypeScript編寫的。

首先,越來越多的JavaScript程序員喜歡TypeScript。

其次,您使用的工具可以從在TypeScript中編寫的軟件中推斷出很多信息,例如Deno等。

這意味著,當我們例如在VS Code中編寫代碼時,由於VS Code和TypeScript在MicroSoft開發,它們之間有緊密的集成,我們可以獲得類似於在編寫代碼時的類型檢查和高級的智能提示功能。換句話說,編輯器可以以非常有用的方式幫助我們。

與Node.js相似和不同的地方

因Deno基本上是Node.js的替代品,所以直接進行比較是有用的。

相似之處:

  • 二者都是基於V8 Chromium Engine開發的
  • 二者在使用JavaScript進行服務器端開發時都非常出色

不同之處:

  • Node.js是使用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

brew install deno

一旦完成,您將可以使用deno命令。這是您使用deno --help獲得的幫助內容:

[[email protected]](/cdn-cgi/l/email-protection)~> 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(讀取-求值(回應)-打印迴圈)。

這與運行deno repl相同。

您將更常見地使用此命令來執行包含在TypeScript文件中的Deno應用程序。

您可以運行TypeScript(.ts)文件或JavaScript(.js)文件。

如果您對TypeScript不熟悉,不用擔心:Deno是使用TypeScript編寫的,但您可以使用JavaScript編寫自己的“客戶端”應用程序。

如果需要的話,我的TypeScript教程可以幫助您快速上手使用TypeScript。

您的第一個Deno應用程序

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

我發現這非常令人驚奇,而且您甚至不需要編寫一個代碼行,只需運行來自任何URL的命令。

Deno下載該程序,編譯它,然後運行它:

console.log('Welcome to Deno 🦕')

如果您使用瀏覽器打開https://deno.land/std/examples/welcome.ts的URL,您將看到此頁面:

Deno的歡迎頁面

您可能感到困惑,對吧?您可能希望看到一個TypeScript文件,但實際上我們得到了一個網頁。這是因為Deno網站的Web服務器知道您使用的是瀏覽器,因此為您提供了更加用戶友好的頁面。

使用wget之類的工具,例如請求以text/plain而不是text/html格式的文件,來下載同一個URL:

如果您想再次運行程序,它現在已經在Deno中緩存,不需要再次下載:

使用--reload標誌來強制重新加載原始代碼:

deno run有很多不同的選項沒顯示在deno --help中。你需要運行deno run --help來展示它們:

[[email protected]](/cdn-cgi/l/email-protection)~> 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代碼示例

除了上面我們運行的welcome應用程序,Deno網站還提供了一些其他示例供您參考:https://deno.land/std/examples/

在編寫本文時,我們可以找到:

  • cat.ts:將所需的文件作為參數打印出來的內容
  • catj.ts:將所需的文件作為參數打印為JSON格式
  • chat/:一個聊天實現
  • colors.ts:使用顏色的示例
  • curl.ts:打印指定URL的內容的簡單curl實現
  • echo_server.ts:TCP echo服務器
  • gist.ts:將文件發佈到gist.github.com
  • test.ts:一個示例測試套件
  • welcome.ts:一個簡單的console.log語句(我們上面運行的第一個程序)
  • xeval.ts:允許您運行接收到的標准輸入的任何行的任何TypeScript代碼。曾被稱為“deno xeval”但已經從官方命令中刪除。

您的第一個Deno應用程序(真實情況)

現在讓我們編寫一些代碼。

您運行的第一個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/examples/welcome.ts URL上打開該文件,您可以看到此頁面:

console.log('Welcome to Deno 🦕')

如果您用瀏覽器打開https://deno.land/std/examples/welcome.ts URL時,您將看到此頁面:

在當前時刻,您可能會感到困惑,您可能期望看到的是一個TypeScript文件,但實際上我們得到了一個網頁頁面。這是因為Deno網站的Web服務器知道您在使用瀏覽器,因此向您提供了更加友好和具有互動性的頁面。

使用wget等工具,例如以text/plain而不是text/html格式的檔來請求同一URL:

wget --header=Accept:text/plain https://deno.land/std/examples/welcome.ts

此命令將下載名為welcome.ts的文件。

如果要再次運行程序,Deno將首先檢查是否已緩存該程序,如果緩存了則無需再次下載:

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

如果您希望重新加載源代碼,使用--reload標誌:

deno run --reload https://deno.land/std/examples/welcome.ts
deno run --allow-env --allow-net https://deno.land/x/oak/examples/simple_server.ts

deno run有很多不同的選擇在deno --help沒有顯示出來,您需要運行deno run --help才能顯示它們:

這是一個完成示例:

import { run } from "deno";

const process = run({
  args: ["echo", "hello", "world"],
  stdout: "piped",
});

if (process.stdout) {
  const output = await process.output();
  console.log(new TextDecoder().decode(output));
}

process.close();

你可以執行它來查看結果:

deno run echo.ts

Oak示例

來看看如何使用Oak構建REST API。

我們將建立一個非常簡單的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 || "8080";
const HOST = env.HOST || "0.0.0.0";

默認情况下,我們的應用程序將運行在0.0.0.0:8080

現在,我們創建Oak應用程序並啟動它:

const app = new Application();
const router = new Router();

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

app.addEventListener("listen", ({hostname, port}) => {
    console.log(`Listening on ${hostname}:${port}`);
});

await app.listen({ port: parseInt(PORT), hostname: HOST });

現在我們有一個運行的應用程序。

運行以下命令來啟用API:

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

Oak文檔:https://deno.land/x/oak

全局

Deno的官方網站是https://deno.land

API文檔可在https://doc.deno.landhttps://deno.land/typedoc/index.html上找到。

真棒的Deno插件:https://github.com/denolib/awesome-deno

其他一些隨機的小事情

  • Deno提供一個內置的fetch實現,與瀏覽器中的一樣
  • Deno正在開發與Node.js的stdlib的兼容層