Deno 1.0即將發佈,你需要知道的都在這裏了

將近兩年過去了,漫長的等待已接近尾聲。Deno 1.0 的 API 已被凍結,離 5 月 13 日的正式發佈只剩幾十個小時了。

Deno 肯定會因爲自己著名的創建者和富有前瞻性的理念,成爲 JavaScript 領域近期最令人興奮和最具爭議性的軟件產品。

Deno 是一款通用的 JavaScript/TypeScript 編程環境。它彙集了許多最出色的開源技術,並使用一個很小的可執行文件提供了全面的解決方案。

Deno 由 Ryan Dahl 創建,他最出名的頭銜是 Node.js 的幕後策劃者。Deno 充分利用了自 2009 年 Node.js 發佈以來不斷加強的 JavaScript 特性。它還解決了 Ryan 在他的“Node.js 令我感到遺憾的 10 件事”中談到的設計缺陷。有些人稱其爲 Node.js 的續作,儘管作者本人並未提出這種主張。

https://www.youtube.com/watch?v=M3BM9TB-8yA

與用 C++ 編寫的 Node.js 不同,Deno 是用 Rust 編寫的。它建立在 Tokio 平臺之上,並且像 Node.js 一樣使用 V8 引擎執行 JavaScript 代碼。它的一項出衆特性是內置了 TypeScript。儘管它仍需要編譯爲 JavaScript 才能運行,但這是在內部完成的,因此對用戶來說 TypeScript 的行爲就好像它是原生支持的一樣。

入門

要下載 Deno,請按照首頁上的說明操作。要更新到將來的版本,請使用 deno upgrade 命令。

要獲得關於 Deno 子命令的幫助,請使用以下任一命令。

  • 獲取摘要:deno [subcommand] -h
  • 獲取詳細信息:deno [subcommand] --help

在這篇指南中,我們將介紹 Deno 1.0 所提供的所有殺手級功能,並提供如何以最新語法使用它們的示例。我會儘可能使用 TypeScript,等效的 JavaScript 方法大家應該都能看出來。

我相信,看完這篇文章後你會愛上 Deno 的。這份指南應該能爲你提供入門 Deno 所需的一切內容。

安全性

默認情況下,Deno 是安全的。相比之下,Node.js 擁有對文件系統和網絡的完全訪問權限。

要在沒有權限的情況下運行程序,請使用:

deno run file-needing-to-run-a-subprocess.ts

如果代碼需要權限設置,則會提醒你。

error: Uncaught PermissionDenied: access to run a subprocess, run again with the --allow-run flag

Deno 使用命令行選項來顯式許可訪問系統的各個部分。最常用的包括:

  • 環境訪問
  • 網絡訪問
  • 文件系統讀 / 寫訪問
  • 運行一個子進程

要查看權限示例的完整列表,請輸入 deno run -h。

這裏的最佳實踐是在 read、write 和 net 上使用權限白名單。這樣你就可以更清楚地瞭解 Deno 被允許訪問哪些內容。例如,要允許 Deno 在 /etc 目錄中只讀文件,請使用:

deno --allow-read=/etc

使用權限的快捷方式

你可能很快就會厭倦每次運行應用程序時都要顯式啓用權限的操作。要解決這個問題,你可以採用以下任一種方法。

1. 允許所有權限

你可以使用 --allow-all 或其快捷方式 -A 啓用所有權限。我不建議這樣做,因爲它抹除了權限控制所帶來的安全性優勢。

2. 製作一個批處理腳本

爲運行程序所需的最小權限創建一個 bash 腳本。

#!/Bin/Bash

// Allow running subprocesses and file system write access
deno run --allow-run --allow-write mod.ts

這樣做的缺點是你可能需要好幾個腳本,分別用於運行、測試和打包場景。

3. 使用任務運行器

你可以使用 GNU 工具 make 創建一個帶有一組 Deno 命令的文件來處理權限。你還可以使用針對 Deno 的版本 Drake

4. 安裝一個可執行的 Deno 程序

使用 deno install 命令安裝一個 Deno 程序,該程序具有執行所需的所有權限。安裝完成後,你可以從 $PATH 中的任何位置訪問它:

https://deno.land/std/manual.md#installing-executable-scripts

標準庫

Deno 標準庫是由 Deno 項目維護,並保證可用於 Deno 的常用模塊集合。它涵蓋了用戶最常用的常見任務代碼,並且是基於 Go 編程語言提供的標準庫。

JavaScript 一直以來困擾用戶的一個問題就是缺乏標準庫。用戶被迫一次又一次地重新發明輪子,開發人員不得不經常在 npm 上搜索第三方模塊,以解決本應由平臺製造商解決的常見問題。

React 之類的庫可以解決很多複雜問題,這些第三方包很好用。但是對於 UUID 生成等簡單的事情來說,我們最好使用官方的標準庫。這些小型庫可作爲大型庫的構建塊,從而加快了開發速度並減少了開發人員的負擔。曾一度流行的庫被遺棄,留給用戶自己維護或者需要用戶找一個替代品的事情時有發生。實際上,常用的 OSS 包中有 10%到 20%並沒有得到積極維護:

https://blog.tidelift.com/up-to-20-percent-of-your-application-dependencies-may-be-unmaintained

可用模塊及其 npm 等效

Deno 內置了 Typescript

TypeScript 是添加了顯式類型的 JavaScript。任何有效的 JavaScript 代碼也是有效的 TypeScript 代碼,因這個將你的代碼轉換爲 TypeScript 是非常容易的。只需將擴展名更改爲.ts 並開始添加類型即可。

要在 Deno 中使用 TypeScript 無需執行任何操作。沒有 Deno 時,必須將 TypeScript 編譯爲 JavaScript 才能運行。Deno 會在內部爲你完成這個步驟,讓 TypeScript 更容易上手。

使用自己的 tsconfig.json

對於熟悉 TypeScript 的人來說,你會習慣使用 tsconfig.json 文件來提供編譯器選項。使用 Deno 時這是可選的,因爲它已經有了自己的默認配置。如果你使用自己的 tsconfig.json 且與 Deno 衝突,則會收到警報。

這個功能需要 -c 選項和 tsconfig.json。

deno run -c tsconfig.json [file-to-run.ts]

有關默認 tsconfig.json 設置的完整細節,請參見 Deno 手冊

大多數開發人員很高興能看到 Deno 默認使用 strict 模式。除非有不懷好意之人改寫它,否則 Deno 將針對用戶那些草率的編碼實踐儘量提出合理的警告。

Deno 儘可能使用 Web 標準

創建一個 Web 標準需要很長時間,但一旦標準被確定下來,我們就不應該忽略它。雖然框架來來去去,但 Web 標準是會長期存在的。花費在學習標準化 API 上的時間永遠不會白費,因爲沒有人敢於破壞 Web;它可能已經使用了數十年,甚至可能在你剩下的職業生涯中一直髮光發熱。

fetch 這個 Web API 提供了用於提取資源的接口。瀏覽器中有一個 JavaScript fetch() 方法。如果你想在 Node.js 中使用這個標準,則需要使用第三方庫 Node Fetch。在 Deno 中它是內置的,並且就像瀏覽器版本一樣開箱即用。

Deno 1.0 提供以下與 Web 兼容的 API。

  • addEventListener
  • atob
  • btoa
  • clearInterval
  • clearTimeout
  • dispatchEvent
  • fetch
  • queueMicrotask
  • removeEventListener
  • setInterval
  • setTimeout
  • AbortSignal
  • Blob
  • File
  • FormData
  • Headers
  • ReadableStream
  • Request
  • Response
  • URL
  • URLSearchParams
  • console
  • isConsoleInstance
  • location
  • onload
  • onunload
  • self
  • window
  • AbortController
  • CustomEvent
  • DOMException
  • ErrorEvent
  • Event
  • EventTarget
  • MessageEvent
  • TextDecoder
  • TextEncoder
  • Worker
  • ImportMeta
  • Location

這些都可以在程序的頂級範圍內使用。這意味着如果你不去用 Deno() 命名空間上的任何方法,那麼你的代碼應該能同時與 Deno 和瀏覽器兼容。儘管這些 Deno API 並不是都 100%符合其等效的 Web 規範,但這對前端開發人員而言仍然是一個很大的好處。

ECMAScript 模塊

相比 Node.js,Deno 的一項主要進步是它使用了官方的 ECMAScript 模塊標準,而不是老式的 CommonJS。Node.js 直到 2019 年底纔在 13.2.0 版本中啓用 ECMAScript 模塊,但支持還是不夠成熟,並且仍然包含有爭議的.mjs 文件擴展名。

Deno 在模塊系統中使用了現代 Web 標準,從而避免了舊時代的影響。模塊使用 URL 或文件路徑引用,幷包含必需的文件擴展名。例如:

import * as log from "https://deno.land/std/log/mod.ts";
import { outputToConsole } from "./view.ts";

使用文件擴展名的問題

Deno 期望模塊具有文件擴展名,但 TypeScript 沒有。

在任何地方都使用文件擴展名都是合乎邏輯的,並且似乎是顯而易見的方法。不幸的是,實踐中事情要複雜得多。現在,你可以使用 Visual Studio Code Extension 來爲只用 Deno 的項目解決這個問題:

https://marketplace.visualstudio.com/items?itemName=axetroy.vscode-deno

對於 TypeScript 的創建者來說,這個問題似乎引起了爭議。在我們最終放棄 CommonJS 之前,應該是不存在一種快速簡便的解決方案的。

讓我們花點時間嚮明智而古老的編程之神祈禱吧。讓他們廢除這些傳統格式,並懲罰那些損害我們所有人利益的守舊者。

包管理

Deno 中包管理的工作機制是經過徹底的重新設計的。它是去中心化的,不依賴什麼中央存儲庫。任何人都可以託管一個包,就像任何人都可以在 Web 上託管任何類型的文件一樣。

使用像 npm 這樣的中心化存儲庫有優點也有缺點,而 Deno 在這一方面肯定是最能引發爭議的。

Deno 的全新包管理機制

這種機制如此簡單,可能會讓你非常驚訝。

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

具體分析一下。

  • 不再有中心化的包管理器了。你可以直接從 Web 導入 ECMAScript 模塊。
  • 不再有“神奇的”Node.js 模塊解析了。現在語法是明確的,這讓各種事情更容易推理。
  • 不再有 node_modules 目錄。現在依賴項會下載並隱藏在你的硬盤中。如果要刷新緩存並再次下載,只需在命令中添加 --reload。

如果你要與項目代碼一起下載依賴項而不是使用全局緩存,請使用 $DENO_DIR env 變量。

尋找兼容的第三方庫

有一個用戶區是爲與 Deno 兼容的第三方模塊準備的,但是在本文撰寫時它還相當原始。例如,用戶沒法按受歡迎程度或下載數量搜索模塊。我預計這個用戶區將會擴大,或者會出現一些替代的模塊站點。

儘管 Deno 沒有官方支持對 Node.js 的向後兼容性,但也有許多庫和應用程序可以正常用於 Deno。有些是開箱即用的,而另一些則需要一些準備工作。

安裝第三方模塊

Deno 剛誕生不久,其生態系統仍在初步發展階段。在撰寫本文時,我建議將 Pika 作爲在標準庫和用戶庫之後開始尋找兼容模塊的第一站。

Pika 的開發人員已與 Deno 合作,通過稱爲 X-TypeScript-Types的 ECMAScript 模塊提供了 TypeScript 類型。要使用 Pika 查找包:

Package found! However, no web-optimized "module" entry point was found in its package.json manifest.

但 preact 是兼容的。點擊它,然後點擊導入。你應該能看到以下內容:

將 import 語句複製到你的代碼中。

import * as pkg from "https://cdn.pika.dev/preact@^10.3.0";

超越 Package.Json

大多數 JavaScript 生態系統還是圍繞 package.json 建立的。它已經膨脹得很大了,其中包含許多職責,諸如:

  • 保留項目的元數據
  • 列出項目依賴項和版本控制
  • 將依賴項分類爲 dpendencies 或 devDependencies
  • 定義程序的入口點
  • 存儲與項目相關的 Shell 腳本
  • 定義一個類型類別,最近被引入以改進 ECMAScript 模塊支持:
{
  "name": "Project Name", // metadata
  "version": "1.0.0", // metadata
  "description": "My application", // metadata
  "type": "module", // module functionality
  "main": "src/mod.ts", // module functionality
  "scripts": {
    "build": "npm run _copy-build-files && rollup -c",
    "build-watch": "npm run _copy-build-files && rollup -cw"
  }, // scripting functionality
  "license": "gpl-3.0", // metadata
  "devDependencies": {
    "@rollup/plugin-typescript": "^3.1.1",
    "rollup": "^1.32.1",
    "typescript": "^3.8.3"
  }, // versioning and categorizing functionality
  "dependencies": {
    "tplant": "^2.3.3"
  } // versioning and categorizing functionality
}

所有這些實踐隨着時間的流逝而融合在一起,現在成爲了 JavaScript 生態系統的標準運作方式。但我們很容易忘記這並不是官方標準的事實;只有當這些功能成爲必需品時纔會想到它。現在 JavaScript 已經迎頭趕上,是時候重新思考了。Deno 仍無法取代 package.json 的所有功能,但目前有一些解決方案。

使用 deps.ts 和 URL 進行版本控制

針對包版本控制有一個 Deno 約定,那就是使用名爲 deps.ts 的一種特殊文件。在內部,依賴項被重新導出。這就能讓應用程序中的不同模塊都引用相同的源。

不是告訴 npm 要下載哪個模塊版本,而是在 deps.ts 中的 URL 中引用。

export { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
export { green, bold } from "https://deno.land/[email protected]/fmt/colors.ts";

如果要更新任何模塊,你可以在 deps.ts 中更改 URL。例如,將 @v0.39.0 替換爲 @v0.41.0,之後新版本將在所有位置上啓用。如果你改爲直接將 https://deno.land/[email protected]/fmt/colors.ts 導入每個模塊,你就得費勁地遍歷整個應用程序並更改每個引用。"你之前下載的模塊以後不會被篡改"這種假設可能會帶來安全風險。所以我們還可以選擇創建鎖定文件。這將確保新下載的模塊與你最初下載的模塊是一樣的。

Deno doc使用JSDoc處理元數據

JSDoc 於 1999 年發佈,到現在已經過去了 21 年。現在,它是爲 JavaScript 和 TypeScript 編寫文檔的最常用和受支持最廣泛的方法。雖然它不是正式的 Web 標準,但它是 package.json 中所有元數據的一個完美替代品。

/**
 * @file Manages the configuration settings for the widget
 * @author Lucio Fulci
 * @copyright 2020 Intervision
 * @license gpl-3.0
 * @version 1.0
 *

Deno 開箱即用地支持 JSDoc,並將其用於內置的文檔系統。雖然 deno doc 命令現在不使用上面的元數據,但它會讀取函數的描述及其參數的描述。

/**
 * Returns a value of (true?) if the rule is to be included
 *
 * @param key Current key name of rule being checked
 * @param val Current value of rule being checked
 **/

你可以使用 deno doc <文件名>命令來查看程序的文檔。

deno doc mod.ts

function rulesToRemove(key: string, val: any[]): boolean
  Returns a value of if the rule is to be included

在線託管程序時,請使用在線文檔查看器來查閱細節。

Deno 的內置工具

這是對前端開發人員影響最大的領域。JavaScript 工具鏈現在的狀況非常混亂。當你加入 TypeScript 工具鏈時,複雜度甚至會進一步增加。

JavaScript 的最大優勢之一是它不需要編譯,所以可以在瀏覽器中直接運行。這樣你就可以立刻獲得編碼的反饋。入門門檻很低;你只需一個文本編輯器和一個瀏覽器就能編寫軟件了。

不幸的是,這種簡單性和可訪問性已被稱爲過度工具鏈的風氣破壞了。這種風氣已經將 JavaScript 的開發工作變成了一場噩夢。我甚至看過一整套關於配置 Webpack 的課程。這種亂象需要有個盡頭——生命苦短啊。

工具鏈已經混亂到了這樣的程度:許多開發人員迫切希望回到實際編寫代碼的流程,而不是浪費時間鼓搗那些配置文件,併爲應該選擇哪種標準而苦惱不已。針對這一問題的一個新興項目是 Facebook 的 Rome。在撰寫本文時,它還處於起步階段。儘管它可能被證明是有價值的,但 Deno 有潛力成爲更實質性的解決方案。

Deno 本身就是一個完整的生態系統,具有運行時和自己的模塊 / 包管理系統。這樣就有了更大的空間來內置所有工具。下面我們研究一下 1.0 版本中可用的工具,以及如何使用它來減少對第三方庫的依賴並簡化開發流程。

目前尚無法在 Deno 中替換整個前端構建管道,但這一天應該不久就會到來了。

測試

測試運行器(test runner)是使用 Deno.test() 函數內置到 Deno 核心中的。斷言庫是在標準庫中提供的。它包括了你常用的所有斷言,例如 assertEquals() 和 assertStrictEq(),以及一些不太常見的斷言,如 assertThrowsAsync()。

在撰寫本文時,Deno 尚無測試覆蓋功能,並且需要使用第三方工具(如 Denon)來設置監視(watch)模式。

要查看所有的測試運行器選項,請使用 deno test --help 命令。儘管選項數量不多,但很多功能你可能之前在 Mocha 之類的程序中就很熟悉了。例如,–failfast 將在遇到第一個錯誤時停止,而 --filter 可用來過濾要運行的測試。

使用測試運行器

最基本的語法是 deno test。它將在工作目錄中運行以 _test 或.test 結尾的所有文件,擴展名爲.js、.ts、.jsx 或.tsx(例如 example_test.ts)

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

Deno.test({
  name: "testing example",
  fn(): void {
    assertEquals("world", "world");
    assertEquals({ hello: "world" }, { hello: "world" });
  },
});

如果你的代碼使用 DOM,則需要使用 lib: [“dom”, “esnext”] 提供自己的 tsconfig.json。我們將在下面詳細介紹。

格式化

dprint 提供格式化功能,dprint 是 Prettier 的高性能替代品,它複製了所有已有的 Prettier 2.0 規則。

要格式化一個或多個文件,請使用 deno fmt或 Visual Studio Code 擴展(稍後將詳細介紹)。

編譯和打包

Deno 可以使用 deno bundle 從命令行創建一個簡單的打包,但是它也公開了一個內部編譯器 API:

https://deno.land/std/manual.md#compiler-api

因此用戶可以創建自己自定義的輸出。這個 API 現在被標記爲不穩定狀態,因而你需要使用 --unstable 標誌。

儘管 Deno 有一些與 Web 兼容的 API,但它們並不完整。如果要編譯任何引用 DOM 的前端 TypeScript,則需要在編譯或打包時告知 Deno 這些類型。你可以使用編譯器 API 選項 lib。

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1 id="greeter">Replace me</h1>
  </body>
</html>
test-dom.ts
let greeter: HTMLElement | null = document.getElementById("greeter")!; // Please forgive the Non-Null Assertion Operator

greeter.innerText = "Hello world!";
compile.ts
const [errors, emitted] = await Deno.compile("test-dom.ts", undefined, {
  lib: ["dom", "esnext"], // include "deno.ns" for deno namespace
  outDir: "dist",
});

if (errors) {
  console.log("There was an error:");
  console.error(errors);
} else {
  console.log(emitted); // normally we would write the file
}

下面是終端中生成的發射圖輸出。

{
 dist/test-dom.js.map: "{\"version\":3,\"file\":\"test-dom.js\",\"sourceRoot\":\"\",\"sources\":[\"file:///home/david/Downloads/deno-arti...",
 dist/test-dom.js: "\"use strict\";\nlet greeter = document.getElementById(\"greeter\");\ngreeter.innerText = \"Hello world!\";\n..."
}

在上面的示例中,我們編譯了引用 DOM 的 test-dom.ts 文件。在 Deno.compile() 中使用 lib 選項會覆蓋 Deno 使用的所有 lib 默認選項,因而你需要重新添加 esnext 和可選的 deno.ns,才能使用 Deno 命名空間。這一切仍然是試驗性的,但是我希望 bundle 命令能夠發展下去,以處理搖樹之類的事情,並能更像 Rollup.js。

調試

Deno 具有內置的調試功能,但是在撰寫本文時 Visual Studio Code 擴展還不支持它。要開始調試,請按以下步驟手動操作。

  • deno run -A --inspect- brk fileToDebug.ts(注意:對模塊使用最低權限)
  • 在 Chrome 或 Chromium 中打開 chrome://inspect。你會看到下面這樣的頁面:

  • 單擊“inspect”以連接並開始調試你的代碼

文件監視

Deno 使用 Rust notify 庫,通過 Deno.watchFs() API 內置了文件監視功能。Deno 喜歡通過其 API 在後臺處理繁重的工作,並讓用戶以自己喜歡的方式實現他們的代碼。這裏沒有 --watch 標誌,而是需要創建自己的實現或使用第三方模塊。

製作自己的文件監視器時唯一需要注意的是防抖(debouncing)。這個 API 可以連續快速觸發多個事件,並且你可能不想多次執行操作。用戶 Caesar2011 使用 Date.now(),僅用了 23 行 TypeScript 代碼就解決了這個問題:

https://github.com/Caesar2011/rhinoder/blob/master/mod.ts

還有一個更高級的 Deno 文件監視解決方案,稱爲 Denon。它相當於 nodemon。如果你想監視工作區中的更改並重新運行測試,只需輸入:

denon test

Visual Studio Code 插件

到目前爲止,最好的擴展是可以從 Visual Studio MarketPlace 獲取的 axetroy 插件。安裝完成後,在你的項目文件夾中創建文件.vscode/settings.json,並在每個項目上都啓用擴展。

// .vscode/settings.json
{
  "deno.enable": true,
}

現在,你就獲得了全面的 IntelliSense 支持以及開始編程所需的一切。

總結

事實證明,JavaScript 生態系統的快速變化是有利有弊的。從積極的一面來看,今天我們有如此之多的高質量工具可用。消極的一面是,不斷涌現的新框架和庫讓人產生了厭倦的感覺。

Deno 成功地克服了 JavaScript 開發工作中的許多缺陷。以下是其中一些改進。

  • 通過使用 Web 標準,Deno 的 API 未來可期。這給了開發人員信心,讓他們知道自己不會浪費時間去學習很快就會過時的東西;
  • 在 JavaScript 上加入 TypeScript 消除了編譯開銷,並允許更緊密的集成;
  • 內置工具意味着用戶無需浪費時間尋找第三方支持;
  • 去中心化的包管理機制將用戶從 npm 中解放出來,同時相比過時的 CommonJS,ECMAScript 模塊令人心曠神怡。

儘管 Deno 可能還無法完全替代 Node.js,但它已經成爲了可以日常使用的絕佳編程環境。

英文原文

Deno 1.0 what you need to know

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章