deno.com
本頁面內容

Node 和 npm 支援

現代 Node.js 專案在 Deno 中執行時,幾乎不需要重新製作。但是,這兩個執行階段之間存在一些關鍵差異,您可以利用這些差異,在將 Node.js 專案遷移到 Deno 時,使程式碼更簡單、更小。

探索內建的 Node API

使用 Node 的內建模組 跳到標題

Deno 提供了一個相容性層,允許在 Deno 程式中使用 Node.js 內建 API。但是,為了使用它們,您需要將 node: 規範符號新增到任何使用它們的 import 陳述式中

import * as os from "node:os";
console.log(os.cpus());

並使用 deno run main.mjs 執行它 - 您會注意到您獲得的輸出與在 Node.js 中執行程式相同。

更新應用程式中的任何 import 以使用 node: 規範符號,應該可以讓任何使用 Node 內建功能的程式碼像在 Node.js 中一樣運作。

為了使更新現有程式碼更容易,Deno 將為未使用 node: 前綴的 import 提供有用的提示

main.mjs
import * as os from "os";
console.log(os.cpus());
$ deno run main.mjs
error: Relative import path "os" not prefixed with / or ./ or ../
  hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:os").
    at file:///main.mjs:1:21

Deno LSP 在您的編輯器中也提供了相同的提示和其他快速修復。

使用 npm 套件 跳到標題

Deno 透過使用 npm: 規範符號,原生支援匯入 npm 套件。例如

main.js
import * as emoji from "npm:node-emoji";

console.log(emoji.emojify(`:sauropod: :heart:  npm`));

可以使用以下命令執行

$ deno run main.js
🦕 ❤️ npm

deno run 命令之前不需要 npm install,也不會建立 node_modules 資料夾。這些套件也受到與 Deno 中其他程式碼相同的權限限制。

npm 規範符號具有以下格式

npm:[@][/]

有關熱門函式庫的範例,請參閱教學章節

CommonJS 支援 跳到標題

CommonJS 是一種模組系統,其出現早於 ES 模組。雖然我們堅信 ES 模組是 JavaScript 的未來,但有數百萬個 npm 函式庫是用 CommonJS 寫成的,而 Deno 為它們提供完整的支援。Deno 將自動判斷套件是否使用 CommonJS,並在匯入時使其無縫運作

main.js
import react from "npm:react";
console.log(react);
$ deno run -E main.js
18.3.1

npm:react 是一個 CommonJS 套件。Deno 允許您像匯入 ES 模組一樣匯入它。

Deno 強烈建議在您的程式碼中使用 ES 模組,但提供具有以下限制的 CommonJS 支援

當使用 CommonJS 模組時,Deno 的權限系統仍然有效。 可能需要提供至少 --allow-read 權限,因為 Deno 將探測檔案系統以尋找 package.json 檔案和 node_modules 目錄,以正確解析 CommonJS 模組。

使用 .cjs 副檔名 跳到標題

如果檔案副檔名為 .cjs,Deno 將把此模組視為 CommonJS。

main.cjs
const express = require("express");

Deno 不會尋找 package.json 檔案和 type 選項來判斷檔案是 CommonJS 還是 ESM。

當使用 CommonJS 時,Deno 預期相依性將被手動安裝,並且 node_modules 目錄將會存在。最好在您的 deno.json 中設定 "nodeModulesDir": "auto" 以確保這一點。

$ cat deno.json
{
  "nodeModulesDir": "auto"
}

$ deno install npm:express
Add npm:express@5.0.0

$ deno run -R -E main.cjs
[Function: createApplication] {
  application: {
    init: [Function: init],
    defaultConfiguration: [Function: defaultConfiguration],
    ...
  }
}

-R-E 標誌用於允許讀取檔案和環境變數的權限。

package.json type 選項 跳到標題

如果檔案旁邊或在具有 package.json 檔案的專案中,目錄樹中有一個具有 "type": "commonjs" 選項的 package.json 檔案,Deno 將嘗試將 .js.jsx.ts.tsx 檔案載入為 CommonJS。

package.json
{
  "type": "commonjs"
}
main.js
const express = require("express");

像 Next.js 的 bundler 等工具會自動產生這樣的 package.json 檔案。

如果您有現有的專案使用 CommonJS 模組,您可以透過在 package.json 檔案中新增 "type": "commonjs" 選項,使其與 Node.js 和 Deno 都能運作。

始終偵測檔案是否可能為 CommonJS 跳到標題

透過在 Deno >= 2.1.2 中使用 --unstable-detect-cjs 執行,可以告訴 Deno 分析模組是否可能為 CommonJS。這將生效,除非存在具有 { "type": "module" }package.json 檔案。

在檔案系統上尋找 package.json 檔案並分析模組以偵測其是否為 CommonJS,比不這樣做需要更長的時間。由於這個原因,並且為了不鼓勵使用 CommonJS,Deno 預設不執行此行為。

手動建立 require() 跳到標題

另一種選擇是手動建立 require() 函數的實例

main.js
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const express = require("express");

在這種情況下,與執行 .cjs 檔案時適用相同的要求 - 相依性需要手動安裝並給予適當的權限標誌。

require(ESM) 跳到標題

Deno 的 require() 實作支援 require ES 模組。

這與 Node.js 中的運作方式相同,您只能 require() 模組圖中沒有 Top-Level Await 的 ES 模組 - 或者換句話說,您只能 require() "同步" 的 ES 模組。

greet.js
export function greet(name) {
  return `Hello ${name}`;
}
esm.js
import { greet } from "./greet.js";

export { greet };
main.cjs
const esm = require("./esm");
console.log(esm);
console.log(esm.greet("Deno"));
$ deno run -R main.cjs
[Module: null prototype] { greet: [Function: greet] }
Hello Deno

匯入 CommonJS 模組 跳到標題

您也可以在 ES 模組中匯入 CommonJS 檔案。

greet.cjs
module.exports = {
  hello: "world",
};
main.js
import greet from "./greet.js";
console.log(greet);
$ deno run main.js
{
  "hello": "world"
}

提示和建議

當使用 CommonJS 模組時,Deno 將提供有用的提示和建議,以引導您朝向可運作的程式碼。

例如,如果您嘗試執行一個沒有 .cjs 副檔名或沒有具有 { "type": "commonjs" }package.json 的 CommonJS 模組,您可能會看到這個

main.js
module.exports = {
  hello: "world",
};
$ deno run main.js
error: Uncaught (in promise) ReferenceError: module is not defined
module.exports = {
^
    at file:///main.js:1:1

    info: Deno supports CommonJS modules in .cjs files, or when the closest
          package.json has a "type": "commonjs" option.
    hint: Rewrite this module to ESM,
          or change the file extension to .cjs,
          or add package.json next to the file with "type": "commonjs" option,
          or pass --unstable-detect-cjs flag to detect CommonJS when loading.
    docs: https://deno-docs.dev.org.tw/go/commonjs

匯入類型 跳到標題

許多 npm 套件都附帶類型,您可以匯入這些類型並直接與類型一起使用

import chalk from "npm:chalk@5";

有些套件不附帶類型,但您可以使用 @ts-types 指令指定其類型。例如,使用 @types 套件

// @ts-types="npm:@types/express@^4.17"
import express from "npm:express@^4.17";

模組解析

官方 TypeScript 編譯器 tsc 支援不同的 moduleResolution 設定。Deno 僅支援現代的 node16 解析。不幸的是,許多 npm 套件無法在 node16 模組解析下正確提供類型,這可能會導致 deno check 報告類型錯誤,而 tsc 不會報告。

如果從 npm: 匯入的預設匯出似乎具有錯誤的類型(正確的類型似乎在 .default 屬性下可用),則很可能是該套件在 node16 模組解析下為來自 ESM 的匯入提供了錯誤的類型。您可以透過檢查錯誤是否也發生在 tsc --module node16package.json 中的 "type": "module" 中,或透過查閱 Are the types wrong? 網站(特別是 "node16 from ESM" 行)來驗證這一點。

如果您想使用不支援 TypeScript 的 node16 模組解析的套件,您可以

  1. 在套件的問題追蹤器上開啟關於此問題的 issue。(也許可以貢獻修復 😃 (雖然,不幸的是,缺乏工具來支援 ESM 和 CJS,因為預設匯出需要不同的語法。另請參閱 microsoft/TypeScript#54593))
  2. 使用 CDN,它會為 Deno 支援重建套件,而不是使用 npm: 識別符號。
  3. 使用 // @ts-expect-error// @ts-ignore 忽略您程式碼庫中收到的類型錯誤。

包含 Node 類型 跳到標題

Node 附帶許多內建類型,例如 Buffer,這些類型可能會在 npm 套件的類型中被引用。要載入這些類型,您必須將類型參考指令新增到 @types/node 套件

/// <reference types="npm:@types/node" />

請注意,在大多數情況下,不指定版本是可以的,因為 Deno 會嘗試使其與其內部的 Node 程式碼保持同步,但如有必要,您可以隨時覆寫使用的版本。

可執行 npm 腳本 跳到標題

具有 bin 條目的 npm 套件可以從命令列執行,而無需使用以下格式的規範符號進行 npm install

npm:[@][/]

例如

$ deno run --allow-read npm:cowsay@1.5.0 "Hello there!"
 ______________
< Hello there! >
 --------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

$ deno run --allow-read npm:cowsay@1.5.0/cowthink "What to eat?"
 ______________
( What to eat? )
 --------------
        o   ^__^
         o  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

node_modules 跳到標題

當您執行 npm install 時,npm 會在您的專案中建立一個 node_modules 目錄,其中包含 package.json 檔案中指定的相依性。

Deno 使用 npm 規範符號將 npm 套件解析為中央全域 npm 快取,而不是在您的專案中使用 node_modules 資料夾。這是理想的,因為它使用更少的空間並保持您的專案目錄乾淨。

但是,在某些情況下,您可能需要在 Deno 專案中使用本機 node_modules 目錄,即使您沒有 package.json(例如,當使用像 Next.js 或 Svelte 這樣的框架,或當相依於使用 Node-API 的 npm 套件時)。

預設 Deno 相依性行為 跳到標題

預設情況下,當您使用 deno run 命令時,Deno 不會建立 node_modules 目錄,相依性將安裝到全域快取中。這是新 Deno 專案的建議設定。

自動建立 node_modules 跳到標題

如果您需要在您的專案中使用 node_modules 目錄,您可以使用 --node-modules-dir 標誌或設定檔中的 nodeModulesDir: auto 選項來告訴 Deno 在目前的工作目錄中建立 node_modules 目錄

deno run --node-modules-dir=auto main.ts

或使用設定檔

deno.json
{
  "nodeModulesDir": "auto"
}

自動模式會自動將相依性安裝到全域快取中,並在專案根目錄中建立本機 node_modules 目錄。這建議用於具有依賴於 node_modules 目錄的 npm 相依性的專案 - 主要是使用 bundler 的專案或具有 postinstall 腳本的 npm 相依性的專案。

手動建立 node_modules 跳到標題

如果您的專案有一個 package.json 檔案,您可以使用手動模式,這需要一個安裝步驟來建立您的 node_modules 目錄

deno install
deno run --node-modules-dir=manual main.ts

或使用設定檔

deno.json
{ "nodeModulesDir": "manual" }

然後您將執行 deno install/npm install/pnpm install 或任何其他套件管理器來建立 node_modules 目錄。

手動模式是使用 package.json 的專案的預設模式。您可能會從 Node.js 專案中認出此工作流程。建議用於使用像 Next.js、Remix、Svelte、Qwik 等框架,或像 Vite、Parcel 或 Rollup 等工具的專案。

注意

我們建議您使用預設的 none 模式,如果 node_modules 目錄內缺少套件而出現錯誤,則回退到 automanual 模式。

搭配 Deno 1.X 使用 node_modules 跳到標題

使用 --node-modules-dir 標誌。

例如,假設有 main.ts

import chalk from "npm:chalk@5";

console.log(chalk.green("Hello"));
deno run --node-modules-dir main.ts

執行上述命令,使用 --node-modules-dir 標誌,將在目前目錄中建立一個 node_modules 資料夾,其資料夾結構與 npm 類似。

Node.js 全域物件 跳到標題

在 Node.js 中,有許多 全域物件在所有程式的範圍內都可用,這些物件特定於 Node.js,例如 process 物件。

以下是一些您可能在常見情況中遇到的全域物件,以及如何在 Deno 中使用它們

  • process - Deno 提供 process 全域物件,這是迄今為止在流行的 npm 套件中最常用的全域物件。它適用於所有程式碼。但是,Deno 將引導您從 node:process 模組顯式匯入它,方法是提供程式碼檢查警告和快速修復
process.js
console.log(process.versions.deno);
$ deno run process.js
2.0.0
$ deno lint process.js
error[no-process-global]: NodeJS process global is discouraged in Deno
 --> /process.js:1:13
  |
1 | console.log(process.versions.deno);
  |             ^^^^^^^
  = hint: Add `import process from "node:process";`

  docs: https://deno-docs.dev.org.tw/lint/rules/no-process-global


Found 1 problem (1 fixable via --fix)
Checked 1 file
  • require() - 請參閱 CommonJS 支援

  • Buffer - 要使用 Buffer API,需要從 node:buffer 模組顯式匯入

buffer.js
import { Buffer } from "node:buffer";

const buf = new Buffer(5, "0");

建議改用 Uint8Array 或其他 TypedArray 子類別。

  • __filename - 改用 import.meta.filename

  • __dirname - 改用 import.meta.dirname

Node-API 附加元件 跳到標題

Deno 支援 Node-API 附加元件,這些附加元件被像 esbuildnpm:sqlite3npm:duckdb 這樣的熱門 npm 套件使用。

您可以期望所有使用公開且有文件記錄的 Node-API 的套件都能運作。

資訊

大多數使用 Node-API 附加元件的套件都依賴 npm "生命週期腳本",例如 postinstall

雖然 Deno 支援它們,但由於安全考量,預設情況下它們不會執行。在 deno install 文件中閱讀更多內容。

截至 Deno 2.0,使用 Node-API 附加元件的 npm 套件僅在存在 node_modules/ 目錄時才受支援。在您的 deno.json 檔案中新增 "nodeModulesDir": "auto""nodeModulesDir": "manual" 設定,或使用 --node-modules-dir=auto|manual 標誌執行,以確保這些套件正常運作。如果設定錯誤,Deno 將提供關於如何解決情況的提示。

從 Node 遷移到 Deno 跳到標題

使用 Deno 執行您的 Node.js 專案是一個直接的過程。在大多數情況下,如果您的專案是使用 ES 模組編寫的,您可以預期幾乎不需要進行任何更改。

需要注意的要點包括

  1. 匯入 Node.js 內建模組需要 node: 規範符號
// ❌
import * as fs from "fs";
import * as http from "http";

// ✅
import * as fs from "node:fs";
import * as http from "node:http";

提示

建議無論如何都要在您現有的專案中更改這些匯入規範符號。這也是在 Node.js 中匯入它們的建議方式。

  1. 某些 Node.js 中可用的全域物件需要顯式匯入,例如 Buffer
import { Buffer } from "node:buffer";
  1. require() 僅在具有 .cjs 副檔名的檔案中可用,在其他檔案中,需要手動建立 require() 的實例。npm 相依性可以使用 require(),無論檔案副檔名如何。

執行腳本 跳到標題

Deno 原生支援使用 deno task 子命令執行 npm 腳本(如果您是從 Node.js 遷移過來的,這類似於 npm run script 命令)。考慮以下 Node.js 專案,其 package.json 內部有一個名為 start 的腳本

package.json
{
  "name": "my-project",
  "scripts": {
    "start": "eslint"
  }
}

您可以使用 Deno 執行此腳本,方法是執行

deno task start

可選的改進 跳到標題

Deno 的核心優勢之一是統一的工具鏈,它開箱即用支援 TypeScript,以及程式碼檢查器、格式化程式和測試執行器等工具。切換到 Deno 可以讓您簡化工具鏈,並減少專案中移動元件的數量。

設定

Deno 有自己的設定檔,deno.jsondeno.jsonc,可用於設定您的專案

您可以使用它透過 imports 選項定義相依性 - 您可以從 package.json 中逐個遷移您的相依性,或者選擇完全不在設定檔中定義它們,而是在您的程式碼中內聯使用 npm: 規範符號。

除了指定相依性之外,您還可以使用 deno.json 來定義任務、程式碼檢查和格式化選項、路徑對應和其他執行階段設定。

程式碼檢查

Deno 附帶一個內建的程式碼檢查器,其編寫時考慮了效能。它類似於 ESLint,但規則數量有限。如果您不依賴 ESLint 外掛程式,您可以從 package.jsondevDependencies 區段中刪除 eslint 相依性,並改用 deno lint

Deno 可以在幾毫秒內檢查大型專案。您可以透過執行以下命令在您的專案上試用它

deno lint

這將檢查您專案中的所有檔案。當程式碼檢查器偵測到問題時,它將在您的編輯器和終端輸出中顯示該行。一個可能的範例

error[no-constant-condition]: Use of a constant expressions as conditions is not allowed.
 --> /my-project/bar.ts:1:5
  | 
1 | if (true) {
  |     ^^^^
  = hint: Remove the constant expression

  docs: https://deno-docs.dev.org.tw/lint/rules/no-constant-condition


Found 1 problem
Checked 4 files

許多程式碼檢查問題可以透過傳遞 --fix 標誌自動修復

deno lint --fix

所有支援的程式碼檢查規則的完整列表可以在 https://deno-docs.dev.org.tw/lint/ 上找到。要了解有關如何設定程式碼檢查器的更多資訊,請查看 deno lint 子命令

格式化

Deno 附帶一個 內建的格式化程式,它可以選擇根據 Deno 樣式指南格式化您的程式碼。您可以改用 Deno 的內建零設定程式碼格式化程式 deno fmt,而不是將 prettier 新增到您的 devDependencies

您可以透過執行以下命令在您的專案上執行格式化程式

deno fmt

如果在 CI 中使用 deno fmt,您可以傳遞 --check 參數,以在格式化程式偵測到格式不正確的程式碼時,使其以錯誤退出。

deno fmt --check

格式化規則可以在您的 deno.json 檔案中設定。要了解有關如何設定格式化程式的更多資訊,請查看 deno fmt 子命令

測試

Deno 鼓勵為您的程式碼編寫測試,並提供一個內建的測試執行器,使編寫和執行測試變得容易。測試執行器與 Deno 緊密整合,因此您不必進行任何額外的設定即可使 TypeScript 或其他功能運作。

my_test.ts
Deno.test("my test", () => {
  // Your test code here
});
deno test

當傳遞 --watch 標誌時,測試執行器將在任何匯入的模組更改時自動重新載入。

要了解有關測試執行器以及如何設定它的更多資訊,請查看 deno test 子命令文件。

私有註冊表 跳到標題

注意

不要與私有儲存庫和模組混淆。

Deno 支援私有註冊表,允許您託管和分享您自己的模組。這對於想要保持程式碼私有化的組織或想要與特定群體分享程式碼的個人很有用。

什麼是私有註冊表? 跳到標題

大型組織通常託管自己的私有 npm 註冊表,以安全地管理內部套件。這些私有註冊表充當組織可以發布和儲存其專有或自訂套件的儲存庫。與公共 npm 註冊表不同,私有註冊表僅限於組織內經過授權的使用者訪問。

如何在 Deno 中使用私有註冊表 跳到標題

首先,設定您的 .npmrc 檔案以指向您的私有註冊表。.npmrc 檔案必須位於專案根目錄或 $HOME 目錄中。將以下內容新增到您的 .npmrc 檔案

@mycompany:registry=http://mycompany.com:8111/
//mycompany.com:8111/:_auth=secretToken

http://mycompany.com:8111/ 替換為您的私有註冊表的實際 URL,並將 secretToken 替換為您的身份驗證令牌。

然後更新您的 deno.jsonpackage.json 以指定您的私有套件的匯入路徑。例如

deno.json
{
  "imports": {
    "@mycompany/package": "npm:@mycompany/package@1.0.0"
  }
}

或者如果您使用 package.json

package.json
{
  "dependencies": {
    "@mycompany/package": "1.0.0"
  }
}

現在您可以在您的 Deno 程式碼中匯入您的私有套件

main.ts
import { hello } from "@mycompany/package";

console.log(hello());

並使用 deno run 命令執行它

deno run main.ts

Node 到 Deno 速查表 跳到標題

Node.js Deno
node file.js deno file.js
ts-node file.ts deno file.ts
nodemon deno run --watch
node -e deno eval
npm i / npm install deno install
npm install -g deno install -g
npm run deno task
eslint deno lint
prettier deno fmt
package.json deno.jsonpackage.json
tsc deno check ¹
typedoc deno doc
jest / ava / mocha / tap / etc deno test
nexe / pkg deno compile
npm explain deno info
nvm / n / fnm deno upgrade
tsserver deno lsp
nyc / c8 / istanbul deno coverage
效能基準測試 deno bench

¹ 類型檢查自動進行,TypeScript 編譯器內建於 deno 二進位檔中。

您找到需要的資訊了嗎?

隱私權政策