2024 年你可以使用的十大 Node.js 現代特性
服務器端 JavaScript 運行時進來充滿了創新,例如 Bun 憑借兼容的 Node.js API 取得了長足進步,而 Node.js 運行時則進一步提供了豐富的標準庫和運行時功能。
時間進入 2024 年,是時候了解 Node.js 運行時所提供的最新特性和功能了。這樣做不僅是為了“與時俱進”,更是為了利用現代 API 的力量來編寫更高效、性能更安全的代碼。
接下來我們將詳細探討每個開發人員在 2024 年都應該開始使用的 10 項現代 Node.js 運行時功能。
先決條件:Node.js LTS 版本
在開始探索這些現代功能之前,請確保您使用的是 Node.js LTS(長期支持)版本。在撰寫本文時,最新的 Node.js LTS 版本是 v20.14.0。
使用以下指令,檢查 Node.js 版本:
$ node --version
v20.14.0
如果您當前沒有使用 LTS 版本,可以考慮使用 fnm 或 nvm 等版本管理器在不同 Node.js 版本之間輕松切換。
Node.js 20 有哪些新功能?
以下我們將介紹 Node.js 最新版本中引入的一些新功能。有些是穩定的,有些仍處于實驗階段,還有一些在之前的版本中就已經得到了支持,只不過你可能還還沒有聽說過。
討論的主題包括:
- Node.js 測試運行器(test runner)
- Node.js 原生 mocking
- Node.js 原生測試覆蓋率
- Node.js 監聽模式(watch mode)
- Node.js corepack[2]
- Node.js import.meta.file:訪問 __dirname 和 __file
- Node.js 原生計時器 Promise
- Node.js 權限模塊(permissions module)
- Node.js 策略模塊(policy module)
Node.js 測試運行器
在 Node.js 引入原生測試運行支持之前,你會用什么工具進行測試呢?當然,這里有一些流行的選項:vitest、jest、mocha 或者 node-tap。
下面,我們將了解如何在開發工作流程中使用 Node.js 原生測試運行器進行測試。
首先,你需要將 Node.js 中的 test 模塊導入到測試文件中,如下所示:
import { test } from 'node:test';
使用 node:test 運行單個測試
要創建單個測試,你可以使用 test 函數,傳入測試的名稱和回調函數。回調函數是你定義測試邏輯的地方。
import { test } from "node:test";
import assert from "node:assert";
import { add } from "../src/math.js";
test("should add two numbers", () => {
const result = add(1, 2);
assert.strictEqual(result, 3);
});
test("should fail to add strings", () => {
assert.throws(() => {
add("1", "2");
});
});
要運行此測試,請使用 node --test [測試文件] 命令:
node --test tests/math.test.js
Node.js 測試運行程序可以自動檢測并運行項目中的測試文件。按照約定,這些文件應以 .test.js 結尾,但這個約定并不需要嚴格遵守。
如果省略測試文件位置參數(positional argument),那么 Node.js 測試運行程序將應用一些啟發式和 glob 模式匹配來查找測試文件——例如 test//tests/ 中的所有文件,或是帶有 test- 前綴或 .test 后綴的文件夾或文件。
你還可以通過 glob 語法匹配測試文件:
node --test '**/*.test.js'
通過 node:assert 使用斷言
Node.js 測試運行程序通過內置的 assert 模塊支持斷言。你可以使用 assert.strictEqual 等不同方法來驗證測試結果。
import assert from 'node:assert';
test('Test 1', () => {
assert.strictEqual(1 + 1, 2);
});
運行測試套件 & 使用測試 hooks 函數
describe 函數用于將相關測試分組到測試套件中。這使您的測試更有組織性并且更易于管理。
import { test, describe } from "node:test";
describe('My Test Suite', () => {
test('Test 1', () => {
// Test 1 logic
});
test('Test 2', () => {
// Test 2 logic
});
});
測試 hooks 函數是在測試之前或之后運行的特殊函數,它們對于設置或清理測試環境很有用。
test.beforeEach(() => {
// Runs before each test
});
test.afterEach(() => {
// Runs after each test
});
你還可以選擇使用 test.skip 函數跳過測試。這在某些你想暫時忽略特定測試時很有幫助。
test.skip('My skipped test', () => {
// Test logic
});
此外,Node.js 測試運行器提供了不同的報告器(reporter),以各種方式格式化和顯示測試結果,使用 --reporter 選項指定。
node --test --test-reporter=tap
Jest 的缺陷
雖然 Jest 是 Node.js 社區中流行的測試框架,但它具有某些缺點,讓原生 Node.js 測試運行器成為更具吸引力的選擇。
Jest 即使作為一個開發依賴項安裝后,你將擁有一個包含不同許可證的 277 個其他依賴項,這些許可證包括 MIT、Apache-2.0、CC-BY-4.0 和 1 個未知許可證。你知道嗎?
圖片
- Jest 會修改全局變量,這可能會導致測試中出現意外行為。
- instanceof 運算符在 Jest 中并不總是按預期工作
- Jest 為你的項目引入了大量的依賴項,你需要關心使用過程中的安全問題和開發時依賴項可能會引起的其他問題
- Jest 可能比原生 Node.js 測試運行器要慢
原生 Node.js 測試運行器的其他強大功能包括運行子測試和并發測試。
子測試允許每個 test() 回調接收一個 context 參數,這個參數允許你使用 context.test 方式創建嵌套測試。
如果你知道如何很好地使用并發測試并能避免競爭條件(racing condition),那么并發測試是一個很棒的功能。只需將 concurrency: true 作為第二個參數傳遞給 describe() 測試套件即可。
什么是測試運行器?
測試運行器是一種軟件工具,允許開發人員對其代碼進行管理并執行自動化測試。 Node.js 測試運行程序幫助你與 Node.js 可以無縫協作,為在 Node.js 應用程序上編寫和運行測試提供了一條龍服務。
Node.js 原生 mocking
Mocking(模擬)是開發人員用來隔離代碼進行測試的一種策略,Node.js 運行時引入了原生模擬功能,這對開發人員更加有效地理解和操作代碼幫助很多。
在此之前,你可能使用過其他測試框架的模擬功能,例如 Jest 的 jest.spyOn 或 mockResolvedValueOncel。當你想要避免在測試中運行實際代碼(例如 HTTP 請求或文件系統 API)并期望稍后可以調用過程時,模擬就非常有用了。
與其他 Node.js 運行時功能(例如監聽和測試覆蓋率功能)不同,模擬并未聲明為實驗性的。但是,它后續可能會迎來更多更改,因為它實在 Node.js 18 中才引入的新功能。
使用 node:test 中的 mock 進行原生模擬測試
讓我們看看如何在實際示例中使用 Node.js 原生模擬功能。測試運行器和(模塊)模擬功能現已作為穩定功能在 Node.js 20 LTS 中提供了。
我們會用到一個工具模塊 dotenv.js,它的作用是從 .env 文件加載環境變量。下面,我們將使用一個測試文件 dotenv.test.js 來測試 dotenv.js 模塊。
dotenv.js 內容如下:
import fs from "node:fs/promises";
export async function loadEnv(path = ".env") {
const rawDataEnv = await fs.readFile(path, "utf8");
const env = {};
rawDataEnv.split("\n").forEach((line) => {
const [key, value] = line.split("=");
env[key] = value;
});
return env;
}
在 dotenv.js 文件中,我們有一個異步函數 loadEnv ,它使用 fs.readFile 方法讀取文件并將文件內容拆分為鍵值對保存在 env 對象中并返回。
現在,讓我們看看如何使用 Node.js 中的原生模擬功能來測試這個函數。
// dotenv.test.js
import { describe, test, mock } from "node:test";
import assert from "node:assert";
import fs from "node:fs/promises";
import { loadEnv } from "../src/dotenv.js";
describe("dotenv test suite", () => {
test("should load env file", async () => {
const mockImplementation = async (path) => {
return "PORT=3000\n";
};
const mockedReadFile = mock.method(fs, "readFile", mockImplementation);
const env = await loadEnv(".env");
assert.strictEqual(env.PORT, "3000");
assert.strictEqual(mockedReadFile.mock.calls.length, 1);
});
});
在測試文件中,我們從 node:test 導入 mock 方法,用它來創建 fs.readFile 的模擬實現。在模擬實現中,無論傳遞的文件路徑如何,我們都會返回一個字符串 "PORT=3000\n"。
然后我們調用 loadEnv 函數,并使用 assert 模塊,我們檢查 2 個地方:
- 返回的對象有一個值為 "3000" 的 PORT 屬性
- fs.readFile 方法只被調用了一次
通過使用 Node.js 中的原生模擬功能,我們能夠有效地將 loadEnv 函數與文件系統隔離并單獨測試。Node.js 20 的模擬功能還支持模擬計時器。
什么是 mocking?
在軟件測試中,模擬是用自定義實現替換特定模塊的實際功能的過程。主要目標是將正在測試的代碼與外部依賴項隔離,確保測試僅驗證單元的功能而不驗證依賴項。模擬還能幫助你你模擬不同的測試場景,例如來自依賴項的錯誤,這可能很難在真實環境驗證。
Node.js 原生測試覆蓋率
什么是測試覆蓋率?
測試覆蓋率是軟件測試中使用的一個指標。它可以幫助開發人員了解應用程序源代碼的測試程度。這很重要,因為它展示了代碼庫中未經測試的區域,使開發人員能夠識別其軟件中的潛在弱點。
為什么測試覆蓋率很重要?因為它是通過減少 BUG 數量和避免回歸來確保軟件的質量。此外,它還可以深入了解測試的有效性,并幫助指導我們構建更強大、可靠和安全的應用程序。
使用原生 Node.js 測試覆蓋率
從 v20 開始,Node.js 運行時包含測試覆蓋率的功能。不過,原生 Node.js 測試覆蓋率目前被標記為實驗性功能,表示雖然現在可用,但在未來版本中可能會發生一些變化。
要使用原生 Node.js 測試覆蓋率,你需要使用 --experimental-coverage 命令行標志。以下示例說明了如何在運行項目測試的 package.json 腳本字段中添加 test:coverage 腳本:
{
"scripts": {
"test": "node --test ./tests",
"test:coverage": "node --experimental-coverage --test ./tests"
}
}
test:coverage 腳本中通過添加 --experimental-coverage 標志就能在執行測試期間生成覆蓋率數據了。
運行 npm run test:coverage 后,你會看到類似于以下內容的輸出:
? tests 7
? suites 4
? pass 5
? fail 0
? cancelled 0
? skipped 1
? todo 1
? duration_ms 84.018917
? start of coverage report
? ---------------------------------------------------------------------
? file | line % | branch % | funcs % | uncovered lines
? ---------------------------------------------------------------------
? src/dotenv.js | 100.00 | 100.00 | 100.00 |
? src/math.js | 100.00 | 100.00 | 100.00 |
? tests/dotenv.test.js | 100.00 | 100.00 | 100.00 |
? tests/math.test.js | 94.64 | 100.00 | 91.67 | 24-26
? ---------------------------------------------------------------------
? all files | 96.74 | 100.00 | 94.44 |
? ---------------------------------------------------------------------
? end of coverage report
這個報告展示了目前的測試所覆蓋的語句、分支、函數和行的百分占比。
Node.js 原生測試覆蓋率是一個強大的工具,可以幫助你提高 Node.js 應用程序的質量。盡管它目前被標記為實驗性功能,但它可以為你的測試覆蓋范圍提供有價值的見解并指導你的測試工作。通過了解和利用這個功能,可以確保你的代碼健壯、可靠且安全。
Node.js 監聽模式
Node.js 監聽模式是一項強大的開發人員功能,允許實時跟蹤 Node.js 文件的更改并自動重新執行腳本。
在深入了解 Node.js 的原生監聽功能之前,有必要了解一下 nodemon[3],它是一個流行的工具程序,有助于滿足 Node.js 早期版本中的這一需求。Nodemon 是一個命令行界面 (CLI) 工具程序,用于在文件目錄中檢測到任何更改時重新啟動 Node.js 應用程序。
npm install -g nodemon
nodemon
這個功能在開發過程中特別有用,避免每次修改文件時手動重新啟動,可以節省時間并提高工作效率。
隨著 Node.js 本身的進步,平臺本身提供了內置功能來實現相同的結果,也不需要在項目中安裝額外的第三方依賴項,例如 nodemon。
值得注意的是,Node.js 中的原生監聽模式功能仍處于實驗階段,未來可能會發生變化。請始終確保你使用的 Node.js 版本支持這個功能。
使用 Node.js 20 原生監聽功能
Node.js 20 使用 --watch 命令行標志引入了原生文件監聽功能。這個功能使用起來很簡單,甚至支持匹配模式來滿足更復雜的文件監聽需要。
node --watch app.js
同事,支持使用 glob 語法做匹配,當你想要監聽一組與特定模式匹配的文件時,這特別有用:
node --watch 'lib/**/*.js' app.js
--watch 標志還可以與 --test 結合使用,這樣修改測試文件時也能重新運行測試:
node --watch --test '**/*.test.js'
需要注意的是,從 Node.js v20 開始,監聽模式功能仍被標記為實驗性的,表示現在功能功能雖然齊全,但后續可能會有修改,不像其他非實驗功能那么穩定或經過優化。在實際s使用時,可能會遇到一些 quirks 或 BUG。
Node.js Corepack
Node.js Corepack 是一個值得探索的有趣功能,它是在 Node.js 16 中引入的,至今仍被標記為實驗性的。
什么是 Corepack?
Corepack 是一個零運行時依賴項目,充當 Node.js 項目和所要使用的包管理器之間的橋梁。安裝后,它提供了一個名為 corepack 的程序,開發人員可以在他們的項目中使用它,確保使用了正確的包管理器,而不必擔心其全局安裝。
為什么要使用 Corepack?
作為 JavaScript 開發人員,我們經常處理多個項目,每個項目可能都有自己首選的包管理器。比如,一個項目使用 pnpm 管理其依賴項,而另一個項目使用 yarn 管理其依賴項,導致你需要在不同的包管理器之間切換。
這個可能就會導致沖突和不一致。Corepack 允許每個項目以無縫的方式指定和使用其首選的包管理器,從而解決了這個問題。
此外,Corepack 在你的項目和全局系統之間提供一層隔離,確保即使全局包升級或刪除,你的項目也能正常運行,這提高了項目的一致性和可靠性。
安裝和使用 Corepack
安裝 Corepack 非常簡單。由于它從版本 16 開始與 Node.js 捆綁在一起,因此你只需安裝或升級 Node.js 到這個或更高版本即可。
安裝后,你可以在 package.json 文件中為項目指定包管理器,如下所示:
{
"packageManager": "yarn@2.4.1"
}
然后,你可以在項目中啟用 Corepack,如下所示:
corepack enable
如果你在項目目錄下的終端中輸入 yarn,即便當前沒有安裝 Yarn,Corepack 會自動檢測并安裝正確版本的。
這種做法會確保始終使用 Yarn v2.4.1 版本來安裝項目的依賴項,無論系統上安裝的全局 Yarn 版本如何。
如果你想全局安裝 Yarn 或使用特定版本,您可以運行:
corepack install --global yarn@stable
Corepack 仍然是一個實驗性功能
盡管在 Node.js 16 中引入了 Corepack,但它仍然被標記為實驗性的。這表示雖然它現在設計并運行的良好,但仍在積極開發中,并且其行為的某些方面將來可能會發生變化。
總之,Corepack 易于安裝、使用簡單,能為你的項目提供額外的可靠性,這絕對是一個值得探索并融入到你的開發工作流程中的功能。
Node.js .env 加載器
應用程序的配置至關重要,作為 Node.js 開發人員,通常會面臨管理 API 憑據、服務器端口號或數據庫等配置的需求。
而我們需要一種方法,能在不改變源代碼的情況下,為不同的環境提供不同的設置。在 Node.js 應用程序中實現這個目的的一種流行方法是使用存儲在 .env 文件中的環境變量。
npm 包 dotenv
在 Node.js 引入對加載 .env 文件的原生支持之前,開發人員主要使用 dotenv npm 包。dotenv 包將環境變量從 .env 文件加載到 process.env 中,然后在整個應用程序中都可訪問了。
以下是 dotenv 包的典型用法:
require('dotenv').config();
console.log(process.env.MY_VARIABLE);
很有用,但需要向你的項目添加額外的依賴項。而在通過引入原生 .env 加載器后,你現在可以直接加載環境變量,而無需任何外部包。
使用原生 .env 加載器
從 Node.js 20 開始,內置了從 .env 文件加載環境變量的功能。這個功能正在積極開發中,但對開發人員來說已經游戲改變者了。
要加載 .env 文件,我們可以在啟動 Node.js 應用程序時使用 --env-file CLI 標志。該標志指定要加載的 .env 文件的路徑。
node --env-file=./.env index.js
這會將環境變量從指定的 .env 文件加載到 process.env 中。然后,這些變量就可以像以前一樣在你的應用程序中那樣使用了。
加載多個 .env 文件
Node.js .env 加載器還支持加載多個 .env 文件。當你針對不同環境(例如開發、測試、生產)有不同的環境變量集時,這非常有用。
你可以指定多個 --env-file 標志來加載多個文件。文件按照指定的順序加載,后面文件中的變量會覆蓋前面文件中的變量。
node --env-file=./.env.default --env-file=./.env.development index.js
在此示例中, ./.env.default 包含默認變量, ./.env.development 包含特定于開發環境的變量。 ./.env.development、./.env.default 中同時存在的變量,前者都將覆蓋后者中的變量。
Node.js 中對加載 .env 文件的原生支持對于 Node.js 開發人員來說是一項重大改進,它簡化了配置管理并消除了對額外軟件包的需要。
開始在 Node.js 應用程序中使用 --env-file CLI 標志并親身體驗便利吧!
Node.js import.meta 支持 __dirname 和 __file
__dirname 和 __file 其實是 CommonJS 模塊中提供的變量,通常用來作為獲取當前文件的目錄名稱和文件路徑的方式。然而,直到最近,這些在 ESM 上還不容易獲得,N你必須使用以下代碼來提取 __dirname:
import url from 'url'
import path from 'path'
const dirname = path.dirname(url.fileURLToPath(import.meta.url))
或者,如果你是 Matteo Collina 的粉絲,您可能已經選擇使用 Matteo 的 desm[4] npm 包。
Node.js 不斷發展,為開發人員提供更有效的方法來處理文件和路徑操作。Node.js v20.11.0 和 Node.js v21.2.0 中引入了一項對 Node.js 開發人員的重大更改,即內置了對 import.meta.dirname 和 import.meta.filename 的支持。
使用 Node.js import.meta.filename 和 import.meta.dirname
值得慶幸的是,隨著 import.meta.filename 和 import.meta.dirname 的引入,這個過程變得更加容易,讓我們看一下使用新功能加載配置文件的示例。
假設你需要加載的 JavaScript 文件所在目錄中有一個 YAML 配置文件,你可以這樣做:
import fs from 'fs';
const { dirname: __dirname, filename: __filename } = import.meta;
const projectSetup = fs.readFileSync(`${__dirname}/setup.yml`, "utf8");
console.log(projectSetup);
在這個例子中,我們使用 import.meta.dirname 來獲取當前文件的目錄名,并將其分配給 __dirname 變量,以便符合 CommonJS 的命名約定。
Node.js 原生計時器 Promise
我們都知道,Node.js 是一種基于 Chrome V8 JavaScript 引擎構建的流行 JavaScript 運行時,始終致力于通過不斷的更新和新功能讓開發人員的生活變得更加輕松。
盡管 Node.js 在 Node.js v15 中就引入了原生計時器 Promise 的支持,但我承認我并沒有經常使用它們。
JavaScript 計時器的簡要回顧
在深入探討本機計時器 Promise 之前,我們來簡要回顧一下 JavaScript setTimeout() 和 setInterval() 計時器。
setTimeout() API 是一個 JavaScript 函數,一旦計時器到期,它就會執行函數或指定的代碼段。
setTimeout(function(){
console.log("Hello World!");
}, 3000);
在上面的代碼中,“Hello World!”將在 3 秒(3000 毫秒)后打印到控制臺。
另一方面,setInterval() 則用于重復執行指定的函數,每次調用之間間隔指定延遲。
setInterval(function(){
console.log("Hello again!");
}, 2000);
在上面的代碼中,“Hello again!”每 2 秒(2000 毫秒)就會打印到控制臺。
舊方法:用 Promise 包裝 setTimeout()
在過去,開發人員通常必須人為地包裝 setTimeout() 函數,并通過 Promise 使用它,這樣做是為了將 setTimeout() 和 async/await 一起使用。
下面是一個封裝的示例:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function demo() {
console.log('Taking a break...');
await sleep(2000);
console.log('Two seconds later...');
}
demo();
這將打印"Taking a break...",等待兩秒鐘,然后打印 "Two seconds later..."。
這么做沒問題,卻給代碼增加了不必要的復雜性。
Node.js Native 計時器 Promise
使用 Node.js Native 計時器 Promise,我們不再需要將 setTimeout() 包裝在 Promise 中。相反,我們可以直接將 setTimeout() 與 async/await 一起使用。
以下是如何使用 Node.js 原生計時器 Promise 的示例:
const {
setTimeout,
} = require('node:timers/promises');
setTimeout(2000, 'Two seconds later...').then((res) => {
console.log(res);
});
console.log('Taking a break...');
在上面的代碼中,setTimeout() 是從 node:timers/promises 導入的。然后我們直接將其與 async/await 一起使用。它將打印"Taking a break...",等待兩秒鐘,然后打印 "Two seconds later..."。
這極大地簡化了異步編程,并使代碼更易于閱讀、編寫和維護。
Node.js 權限模型
目前加入 Node.js TSC 的 Rafael Gonzaga 恢復了 Node.js 權限模塊的開發工作,這個模塊與 Deno 類似,提供了一組進程級的可配置資源約束。
出于安全和合規性原因,管理和控制 Node.js 應用程序可以訪問的資源變得越來越重要。因為有時候你也不知道項目使用的 npm 包突然被黑了,或是誤下載了一個惡意 npm 包等情況的發生。
在這方面,Node.js 引入了一項稱為權限模塊的實驗性功能,用于管理 Node.js 應用程序中的資源權限。使用 --experimental-permission 命令行標志可以啟用這個功能。
Node.js 資源權限模型
Node.js 中的權限模型提供了一套抽象來管理對各種資源(如文件系統、網絡、環境變量和工作線程等)的訪問。當你想要限制應用程序的某個部分可以訪問的資源時,這個功能就很有用。
你可以使用權限模型的常見資源約束,包括:
- 使用 --allow-fs-read=* 和 --allow-fs-write=* 進行文件系統讀寫,可以指定目錄和特定文件路徑,還可以通過重復標志來限定多種資源
- 使用 --allow-child-process 進行子進程調用
- 使用 --allow-worker 進行工作線程調用
Node.js 權限模型還通過 process.permission.has(resource, value) 提供運行時 API,以允許查詢特定訪問權限。
如果你嘗試訪問不能允許的資源,例如讀取 .env 文件,就會看到 ERR_ACCESS_DENIED 錯誤:
> start:protected
> node --env-file=.env --experimental-permission server.js
node:internal/modules/cjs/loader:197
const result = internalModuleStat(filename);
^
Error: Access to this API has been restricted
at stat (node:internal/modules/cjs/loader:197:18)
at Module._findPath (node:internal/modules/cjs/loader:682:16)
at resolveMainPath (node:internal/modules/run_main:28:23)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:24)
at node:internal/main/run_main_module:28:49 {
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: '/Users/lirantal/repos/modern-nodejs-runtime-features-2024/server.js'
}
Node.js v20.14.0
Node.js 權限模型示例
考慮這樣一個場,有一個處理文件上傳的 Node.js 應用程序。你希望限制應用程序的這一部分,以便它只能訪問存儲上傳文件的特定目錄。
使用 --experimental-permission 標志啟動 Node.js 應用程序時啟用實驗性權限功能。
node --experimental-permission ./app.js
我們還想專門允許應用程序讀取 2 個受信任的文件 .env 和 setup.yml ,因此我們需要將上面的內容更新為:
node --experimental-permission --allow-fs-write=/tmp/uploads --allow-fs-read=.env --allow-fs-read=setup.yml ./app.js
這樣一來,如果應用程序對提供的上傳路徑之外的文件進行寫入,就會因報錯而終止。
請參閱以下代碼示例,了解如何通過 try/catch 包裝資源訪問的地方,或通過權限檢 API 來進行防御性編程,確保不會因為權限模型的引入導致出現的異常意外終止程序。
const { dirname: __dirname, filename: __filename } = import.meta;
// @TODO to avoid the Node.js resource permission issue you should update
// the path to be `setup.yml` in the current directory and not `../setup.yml`.
// the outside path for setup.yml was only changed in the source code to
// show you how Node.js resource permission module will halt if trying to access
// something outside the current directory.
const filePath = `${__dirname}/../setup.yml`;
try {
const projectSetup = fs.readFileSync(filePath, "utf8");
// @TODO do something with projectSetup if you want to
} catch (error) {
console.error(error.code);
}
// @TODO or consider using the permissions runtime API check:
if (!process.permission.has("read", filePath)) {
console.error("no permissions to read file at", filePath);
}
值得注意的是,Node.js 中的權限功能仍處于實驗階段,未來可能會發生變化。
Node.js 策略模塊
Node.js 策略模塊是一項安全功能,旨在防止惡意代碼在 Node.js 應用程序中加載和執行。雖然它不會追蹤加載代碼的來源,但它提供了針對潛在威脅的可靠防御機制。
策略模塊利用 --experimental-policy CLI 標志來啟用基于策略的代碼加載。此標志采用策略清單文件(JSON 格式)作為參數。例如, --experimental-policy=policy.json。
策略清單文件包含 Node.js 在加載模塊時遵循的策略,這提供了一種強大的方法來控制加載到應用程序中的代碼的性質。
使用 Node.js 策略模塊
讓我們通過一個簡單的示例來演示如何使用 Node.js 策略模塊:
- 創建策略文件。這個文件應該是一個 JSON 文件,指定應用程序加載模塊的策略。我們稱之為 policy.json。
類似:
{
"resources": {
"./moduleA.js": {
"integrity": "sha384-xxxxx"
},
"./moduleB.js": {
"integrity": "sha384-yyyyy"
}
}
}
這個策略文件指定 moduleA.js 和 moduleB.js 應加載特定的 integrity 值。
然而,為所有直接和間接依賴項生成策略文件并不簡單。幾年前,Bradley Meck 創建了 node-policy npm 包,它提供了一個 CLI 來自動生成策略文件。
- 使用 --experimental-policy 標志運行 Node.js 應用程序
node --experimental-policy=policy.json app.js
這個命令告訴 Node.js 在加載 app.js 中的模塊時遵守 policy.json 中指定的策略。
- 為了防止篡改策略文件,你可以使用 --policy-integrity 標志為策略文件本身提供 integrity 值:
node --experimental-policy=policy.json --policy-integrity="sha384-zzzzz" app.js
這個命令可確保保持策略文件的完整性,即使磁盤上的文件發生更改也是如此。
integrity 策略的注意事項
Node.js 運行時沒有內置功能來生成或管理策略文件,并且可能會帶來困難,例如根據生產環境與開發環境以及動態模塊導入來管理不同的策略。
另一個需要注意的是,如果你已經擁有處于當前狀態的惡意 npm 包,那么生成模塊完整性策略文件就為時已晚。
我個人建議你可以持續關注這方面的更新,并慢慢嘗試逐步采用此功能。
有關 Node.js 策略模塊的更多信息,你可以查看有關向 Node.js 引入實驗性完整性策略[5]這篇文章,其中提供了有關使用 Node.js 策略完整性的更詳細的詳細教程。
總結
當我們瀏覽了你應該在 2024 年開始使用的現代 Node.js 運行時功能時,很明顯,這些功能幫助你簡化開發流程、增強應用程序性能并加強安全性。這些功能不僅僅是時尚,也重新定義了 Node.js 開發方式的巨大潛力。
參考資料
[1]10 modern Node.js runtime features to start using in 2024: https://snyk.io/blog/10-modern-node-js-runtime-features/
[2]corepack: https://nodejs.org/api/corepack.html
[3]nodemon: https://snyk.io/advisor/npm-package/nodemon
[4]desm: https://snyk.io/advisor/npm-package/desm
[5]向 Node.js 引入實驗性完整性策略: https://snyk.io/blog/introducing-experimental-integrity-policies-to-node-js/
原文鏈接:10 modern Node.js runtime features to start using in 2024[1], 2024.5.29, by Liran Tal。翻譯時有刪改。