本頁面內容
Node 和 npm 支援
現代 Node.js 專案在 Deno 中執行時,幾乎不需要重新製作。但是,這兩個執行階段之間存在一些關鍵差異,您可以利用這些差異,在將 Node.js 專案遷移到 Deno 時,使程式碼更簡單、更小。
使用 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 提供有用的提示
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 套件。例如
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,並在匯入時使其無縫運作
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。
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。
{
"type": "commonjs"
}
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()
函數的實例
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 模組。
export function greet(name) {
return `Hello ${name}`;
}
import { greet } from "./greet.js";
export { greet };
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 檔案。
module.exports = {
hello: "world",
};
import greet from "./greet.js";
console.log(greet);
$ deno run main.js
{
"hello": "world"
}
提示和建議
當使用 CommonJS 模組時,Deno 將提供有用的提示和建議,以引導您朝向可運作的程式碼。
例如,如果您嘗試執行一個沒有 .cjs
副檔名或沒有具有 { "type": "commonjs" }
的 package.json
的 CommonJS 模組,您可能會看到這個
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 node16
和 package.json
中的 "type": "module"
中,或透過查閱 Are the types wrong? 網站(特別是 "node16 from ESM" 行)來驗證這一點。
如果您想使用不支援 TypeScript 的 node16 模組解析的套件,您可以
- 在套件的問題追蹤器上開啟關於此問題的 issue。(也許可以貢獻修復 😃 (雖然,不幸的是,缺乏工具來支援 ESM 和 CJS,因為預設匯出需要不同的語法。另請參閱 microsoft/TypeScript#54593))
- 使用 CDN,它會為 Deno 支援重建套件,而不是使用
npm:
識別符號。 - 使用
// @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
或使用設定檔
{
"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
或使用設定檔
{ "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
目錄內缺少套件而出現錯誤,則回退到 auto
或 manual
模式。
搭配 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
模組顯式匯入它,方法是提供程式碼檢查警告和快速修復
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
模組顯式匯入
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 附加元件,這些附加元件被像 esbuild
、npm:sqlite3
或 npm: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 模組編寫的,您可以預期幾乎不需要進行任何更改。
需要注意的要點包括
- 匯入 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 中匯入它們的建議方式。
- 某些 Node.js 中可用的全域物件需要顯式匯入,例如
Buffer
import { Buffer } from "node:buffer";
require()
僅在具有.cjs
副檔名的檔案中可用,在其他檔案中,需要手動建立require()
的實例。npm 相依性可以使用require()
,無論檔案副檔名如何。
執行腳本 跳到標題
Deno 原生支援使用 deno task
子命令執行 npm 腳本(如果您是從 Node.js 遷移過來的,這類似於 npm run script
命令)。考慮以下 Node.js 專案,其 package.json
內部有一個名為 start
的腳本
{
"name": "my-project",
"scripts": {
"start": "eslint"
}
}
您可以使用 Deno 執行此腳本,方法是執行
deno task start
可選的改進 跳到標題
Deno 的核心優勢之一是統一的工具鏈,它開箱即用支援 TypeScript,以及程式碼檢查器、格式化程式和測試執行器等工具。切換到 Deno 可以讓您簡化工具鏈,並減少專案中移動元件的數量。
設定
Deno 有自己的設定檔,deno.json
或 deno.jsonc
,可用於設定您的專案
您可以使用它透過 imports
選項定義相依性 - 您可以從 package.json
中逐個遷移您的相依性,或者選擇完全不在設定檔中定義它們,而是在您的程式碼中內聯使用 npm:
規範符號。
除了指定相依性之外,您還可以使用 deno.json
來定義任務、程式碼檢查和格式化選項、路徑對應和其他執行階段設定。
程式碼檢查
Deno 附帶一個內建的程式碼檢查器,其編寫時考慮了效能。它類似於 ESLint,但規則數量有限。如果您不依賴 ESLint 外掛程式,您可以從 package.json
的 devDependencies
區段中刪除 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 或其他功能運作。
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.json
或 package.json
以指定您的私有套件的匯入路徑。例如
{
"imports": {
"@mycompany/package": "npm:@mycompany/package@1.0.0"
}
}
或者如果您使用 package.json
{
"dependencies": {
"@mycompany/package": "1.0.0"
}
}
現在您可以在您的 Deno 程式碼中匯入您的私有套件
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.json 或 package.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
二進位檔中。