自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

2024 年你可以使用的十大 Node.js 現(xiàn)代特性

開發(fā) 前端
當(dāng)我們?yōu)g覽了你應(yīng)該在 2024 年開始使用的現(xiàn)代 Node.js 運(yùn)行時(shí)功能時(shí),很明顯,這些功能幫助你簡化開發(fā)流程、增強(qiáng)應(yīng)用程序性能并加強(qiáng)安全性。這些功能不僅僅是時(shí)尚,也重新定義了 Node.js 開發(fā)方式的巨大潛力。

服務(wù)器端 JavaScript 運(yùn)行時(shí)進(jìn)來充滿了創(chuàng)新,例如 Bun 憑借兼容的 Node.js API 取得了長足進(jìn)步,而 Node.js 運(yùn)行時(shí)則進(jìn)一步提供了豐富的標(biāo)準(zhǔn)庫和運(yùn)行時(shí)功能。

時(shí)間進(jìn)入 2024 年,是時(shí)候了解 Node.js 運(yùn)行時(shí)所提供的最新特性和功能了。這樣做不僅是為了“與時(shí)俱進(jìn)”,更是為了利用現(xiàn)代 API 的力量來編寫更高效、性能更安全的代碼。

接下來我們將詳細(xì)探討每個(gè)開發(fā)人員在 2024 年都應(yīng)該開始使用的 10 項(xiàng)現(xiàn)代 Node.js 運(yùn)行時(shí)功能。

先決條件:Node.js LTS 版本

在開始探索這些現(xiàn)代功能之前,請確保您使用的是 Node.js LTS(長期支持)版本。在撰寫本文時(shí),最新的 Node.js LTS 版本是 v20.14.0。

使用以下指令,檢查 Node.js 版本:

$ node --version
v20.14.0

如果您當(dāng)前沒有使用 LTS 版本,可以考慮使用 fnm 或 nvm 等版本管理器在不同 Node.js 版本之間輕松切換。

Node.js 20 有哪些新功能?

以下我們將介紹 Node.js 最新版本中引入的一些新功能。有些是穩(wěn)定的,有些仍處于實(shí)驗(yàn)階段,還有一些在之前的版本中就已經(jīng)得到了支持,只不過你可能還還沒有聽說過。

討論的主題包括:

  1. Node.js 測試運(yùn)行器(test runner)
  2. Node.js 原生 mocking
  3. Node.js 原生測試覆蓋率
  4. Node.js 監(jiān)聽模式(watch mode)
  5. Node.js corepack[2]
  6. Node.js import.meta.file:訪問 __dirname 和 __file
  7. Node.js 原生計(jì)時(shí)器 Promise
  8. Node.js 權(quán)限模塊(permissions module)
  9. Node.js 策略模塊(policy module)

Node.js 測試運(yùn)行器

在 Node.js 引入原生測試運(yùn)行支持之前,你會(huì)用什么工具進(jìn)行測試呢?當(dāng)然,這里有一些流行的選項(xiàng):vitest、jest、mocha 或者 node-tap。

下面,我們將了解如何在開發(fā)工作流程中使用 Node.js 原生測試運(yùn)行器進(jìn)行測試。

首先,你需要將 Node.js 中的 test 模塊導(dǎo)入到測試文件中,如下所示:

import { test } from 'node:test';

使用 node:test 運(yùn)行單個(gè)測試

要?jiǎng)?chuàng)建單個(gè)測試,你可以使用 test 函數(shù),傳入測試的名稱和回調(diào)函數(shù)。回調(diào)函數(shù)是你定義測試邏輯的地方。

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");
  });
});

要運(yùn)行此測試,請使用 node --test [測試文件] 命令:

node --test tests/math.test.js

Node.js 測試運(yùn)行程序可以自動(dòng)檢測并運(yùn)行項(xiàng)目中的測試文件。按照約定,這些文件應(yīng)以 .test.js 結(jié)尾,但這個(gè)約定并不需要嚴(yán)格遵守。

如果省略測試文件位置參數(shù)(positional argument),那么 Node.js 測試運(yùn)行程序?qū)?yīng)用一些啟發(fā)式和 glob 模式匹配來查找測試文件——例如 test//tests/ 中的所有文件,或是帶有 test- 前綴或 .test 后綴的文件夾或文件。

你還可以通過 glob 語法匹配測試文件:

node --test '**/*.test.js'

通過 node:assert 使用斷言

Node.js 測試運(yùn)行程序通過內(nèi)置的 assert 模塊支持?jǐn)嘌?。你可以使?assert.strictEqual 等不同方法來驗(yàn)證測試結(jié)果。

import assert from 'node:assert';

test('Test 1', () => {
  assert.strictEqual(1 + 1, 2);
});

運(yùn)行測試套件 & 使用測試 hooks 函數(shù)

describe 函數(shù)用于將相關(guān)測試分組到測試套件中。這使您的測試更有組織性并且更易于管理。

import { test, describe } from "node:test";

describe('My Test Suite', () => {
  test('Test 1', () => {
    // Test 1 logic
  });

  test('Test 2', () => {
    // Test 2 logic
  });
});

測試 hooks 函數(shù)是在測試之前或之后運(yùn)行的特殊函數(shù),它們對于設(shè)置或清理測試環(huán)境很有用。

test.beforeEach(() => {
  // Runs before each test
});

test.afterEach(() => {
  // Runs after each test
});

你還可以選擇使用 test.skip 函數(shù)跳過測試。這在某些你想暫時(shí)忽略特定測試時(shí)很有幫助。

test.skip('My skipped test', () => {
  // Test logic
});

此外,Node.js 測試運(yùn)行器提供了不同的報(bào)告器(reporter),以各種方式格式化和顯示測試結(jié)果,使用 --reporter 選項(xiàng)指定。

node --test --test-reporter=tap

Jest 的缺陷

雖然 Jest 是 Node.js 社區(qū)中流行的測試框架,但它具有某些缺點(diǎn),讓原生 Node.js 測試運(yùn)行器成為更具吸引力的選擇。

Jest 即使作為一個(gè)開發(fā)依賴項(xiàng)安裝后,你將擁有一個(gè)包含不同許可證的 277 個(gè)其他依賴項(xiàng),這些許可證包括 MIT、Apache-2.0、CC-BY-4.0 和 1 個(gè)未知許可證。你知道嗎?

圖片圖片

  1. Jest 會(huì)修改全局變量,這可能會(huì)導(dǎo)致測試中出現(xiàn)意外行為。
  2. instanceof 運(yùn)算符在 Jest 中并不總是按預(yù)期工作
  3. Jest 為你的項(xiàng)目引入了大量的依賴項(xiàng),你需要關(guān)心使用過程中的安全問題和開發(fā)時(shí)依賴項(xiàng)可能會(huì)引起的其他問題
  4. Jest 可能比原生 Node.js 測試運(yùn)行器要慢

原生 Node.js 測試運(yùn)行器的其他強(qiáng)大功能包括運(yùn)行子測試和并發(fā)測試。

子測試允許每個(gè) test() 回調(diào)接收一個(gè) context 參數(shù),這個(gè)參數(shù)允許你使用 context.test 方式創(chuàng)建嵌套測試。

如果你知道如何很好地使用并發(fā)測試并能避免競爭條件(racing condition),那么并發(fā)測試是一個(gè)很棒的功能。只需將 concurrency: true 作為第二個(gè)參數(shù)傳遞給 describe() 測試套件即可。

什么是測試運(yùn)行器?

測試運(yùn)行器是一種軟件工具,允許開發(fā)人員對其代碼進(jìn)行管理并執(zhí)行自動(dòng)化測試。 Node.js 測試運(yùn)行程序幫助你與 Node.js 可以無縫協(xié)作,為在 Node.js 應(yīng)用程序上編寫和運(yùn)行測試提供了一條龍服務(wù)。

Node.js 原生 mocking

Mocking(模擬)是開發(fā)人員用來隔離代碼進(jìn)行測試的一種策略,Node.js 運(yùn)行時(shí)引入了原生模擬功能,這對開發(fā)人員更加有效地理解和操作代碼幫助很多。

在此之前,你可能使用過其他測試框架的模擬功能,例如 Jest 的 jest.spyOn 或 mockResolvedValueOncel。當(dāng)你想要避免在測試中運(yùn)行實(shí)際代碼(例如 HTTP 請求或文件系統(tǒng) API)并期望稍后可以調(diào)用過程時(shí),模擬就非常有用了。

與其他 Node.js 運(yùn)行時(shí)功能(例如監(jiān)聽和測試覆蓋率功能)不同,模擬并未聲明為實(shí)驗(yàn)性的。但是,它后續(xù)可能會(huì)迎來更多更改,因?yàn)樗鼘?shí)在 Node.js 18 中才引入的新功能。

使用 node:test 中的 mock 進(jìn)行原生模擬測試

讓我們看看如何在實(shí)際示例中使用 Node.js 原生模擬功能。測試運(yùn)行器和(模塊)模擬功能現(xiàn)已作為穩(wěn)定功能在 Node.js 20 LTS 中提供了。

我們會(huì)用到一個(gè)工具模塊 dotenv.js,它的作用是從 .env 文件加載環(huán)境變量。下面,我們將使用一個(gè)測試文件 dotenv.test.js 來測試 dotenv.js 模塊。

dotenv.js 內(nèi)容如下:

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 文件中,我們有一個(gè)異步函數(shù) loadEnv ,它使用 fs.readFile 方法讀取文件并將文件內(nèi)容拆分為鍵值對保存在 env 對象中并返回。

現(xiàn)在,讓我們看看如何使用 Node.js 中的原生模擬功能來測試這個(gè)函數(shù)。

// 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 導(dǎo)入 mock 方法,用它來創(chuàng)建 fs.readFile 的模擬實(shí)現(xiàn)。在模擬實(shí)現(xiàn)中,無論傳遞的文件路徑如何,我們都會(huì)返回一個(gè)字符串 "PORT=3000\n"。

然后我們調(diào)用 loadEnv 函數(shù),并使用 assert 模塊,我們檢查 2 個(gè)地方:

  1. 返回的對象有一個(gè)值為 "3000" 的 PORT 屬性
  2. fs.readFile 方法只被調(diào)用了一次

通過使用 Node.js 中的原生模擬功能,我們能夠有效地將 loadEnv 函數(shù)與文件系統(tǒng)隔離并單獨(dú)測試。Node.js 20 的模擬功能還支持模擬計(jì)時(shí)器。

什么是 mocking?

在軟件測試中,模擬是用自定義實(shí)現(xiàn)替換特定模塊的實(shí)際功能的過程。主要目標(biāo)是將正在測試的代碼與外部依賴項(xiàng)隔離,確保測試僅驗(yàn)證單元的功能而不驗(yàn)證依賴項(xiàng)。模擬還能幫助你你模擬不同的測試場景,例如來自依賴項(xiàng)的錯(cuò)誤,這可能很難在真實(shí)環(huán)境驗(yàn)證。

Node.js 原生測試覆蓋率

什么是測試覆蓋率?

測試覆蓋率是軟件測試中使用的一個(gè)指標(biāo)。它可以幫助開發(fā)人員了解應(yīng)用程序源代碼的測試程度。這很重要,因?yàn)樗故玖舜a庫中未經(jīng)測試的區(qū)域,使開發(fā)人員能夠識(shí)別其軟件中的潛在弱點(diǎn)。

為什么測試覆蓋率很重要?因?yàn)樗峭ㄟ^減少 BUG 數(shù)量和避免回歸來確保軟件的質(zhì)量。此外,它還可以深入了解測試的有效性,并幫助指導(dǎo)我們構(gòu)建更強(qiáng)大、可靠和安全的應(yīng)用程序。

使用原生 Node.js 測試覆蓋率

從 v20 開始,Node.js 運(yùn)行時(shí)包含測試覆蓋率的功能。不過,原生 Node.js 測試覆蓋率目前被標(biāo)記為實(shí)驗(yàn)性功能,表示雖然現(xiàn)在可用,但在未來版本中可能會(huì)發(fā)生一些變化。

要使用原生 Node.js 測試覆蓋率,你需要使用 --experimental-coverage 命令行標(biāo)志。以下示例說明了如何在運(yùn)行項(xiàng)目測試的 package.json 腳本字段中添加 test:coverage 腳本:

{
  "scripts": {
    "test": "node --test ./tests",
    "test:coverage": "node --experimental-coverage --test ./tests"
  }
}

test:coverage 腳本中通過添加 --experimental-coverage 標(biāo)志就能在執(zhí)行測試期間生成覆蓋率數(shù)據(jù)了。

運(yùn)行 npm run test:coverage 后,你會(huì)看到類似于以下內(nèi)容的輸出:

? 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

這個(gè)報(bào)告展示了目前的測試所覆蓋的語句、分支、函數(shù)和行的百分占比。

Node.js 原生測試覆蓋率是一個(gè)強(qiáng)大的工具,可以幫助你提高 Node.js 應(yīng)用程序的質(zhì)量。盡管它目前被標(biāo)記為實(shí)驗(yàn)性功能,但它可以為你的測試覆蓋范圍提供有價(jià)值的見解并指導(dǎo)你的測試工作。通過了解和利用這個(gè)功能,可以確保你的代碼健壯、可靠且安全。

Node.js 監(jiān)聽模式

Node.js 監(jiān)聽模式是一項(xiàng)強(qiáng)大的開發(fā)人員功能,允許實(shí)時(shí)跟蹤 Node.js 文件的更改并自動(dòng)重新執(zhí)行腳本。

在深入了解 Node.js 的原生監(jiān)聽功能之前,有必要了解一下 nodemon[3],它是一個(gè)流行的工具程序,有助于滿足 Node.js 早期版本中的這一需求。Nodemon 是一個(gè)命令行界面 (CLI) 工具程序,用于在文件目錄中檢測到任何更改時(shí)重新啟動(dòng) Node.js 應(yīng)用程序。

npm install -g nodemon
nodemon

這個(gè)功能在開發(fā)過程中特別有用,避免每次修改文件時(shí)手動(dòng)重新啟動(dòng),可以節(jié)省時(shí)間并提高工作效率。

隨著 Node.js 本身的進(jìn)步,平臺(tái)本身提供了內(nèi)置功能來實(shí)現(xiàn)相同的結(jié)果,也不需要在項(xiàng)目中安裝額外的第三方依賴項(xiàng),例如 nodemon。

值得注意的是,Node.js 中的原生監(jiān)聽模式功能仍處于實(shí)驗(yàn)階段,未來可能會(huì)發(fā)生變化。請始終確保你使用的 Node.js 版本支持這個(gè)功能。

使用 Node.js 20 原生監(jiān)聽功能

Node.js 20 使用 --watch 命令行標(biāo)志引入了原生文件監(jiān)聽功能。這個(gè)功能使用起來很簡單,甚至支持匹配模式來滿足更復(fù)雜的文件監(jiān)聽需要。

node --watch app.js

同事,支持使用 glob 語法做匹配,當(dāng)你想要監(jiān)聽一組與特定模式匹配的文件時(shí),這特別有用:

node --watch 'lib/**/*.js' app.js

--watch 標(biāo)志還可以與 --test 結(jié)合使用,這樣修改測試文件時(shí)也能重新運(yùn)行測試:

node --watch --test '**/*.test.js'

需要注意的是,從 Node.js v20 開始,監(jiān)聽模式功能仍被標(biāo)記為實(shí)驗(yàn)性的,表示現(xiàn)在功能功能雖然齊全,但后續(xù)可能會(huì)有修改,不像其他非實(shí)驗(yàn)功能那么穩(wěn)定或經(jīng)過優(yōu)化。在實(shí)際s使用時(shí),可能會(huì)遇到一些 quirks 或 BUG。

Node.js Corepack

Node.js Corepack 是一個(gè)值得探索的有趣功能,它是在 Node.js 16 中引入的,至今仍被標(biāo)記為實(shí)驗(yàn)性的。

什么是 Corepack?

Corepack 是一個(gè)零運(yùn)行時(shí)依賴項(xiàng)目,充當(dāng) Node.js 項(xiàng)目和所要使用的包管理器之間的橋梁。安裝后,它提供了一個(gè)名為 corepack 的程序,開發(fā)人員可以在他們的項(xiàng)目中使用它,確保使用了正確的包管理器,而不必?fù)?dān)心其全局安裝。

為什么要使用 Corepack?

作為 JavaScript 開發(fā)人員,我們經(jīng)常處理多個(gè)項(xiàng)目,每個(gè)項(xiàng)目可能都有自己首選的包管理器。比如,一個(gè)項(xiàng)目使用 pnpm 管理其依賴項(xiàng),而另一個(gè)項(xiàng)目使用 yarn 管理其依賴項(xiàng),導(dǎo)致你需要在不同的包管理器之間切換。

這個(gè)可能就會(huì)導(dǎo)致沖突和不一致。Corepack 允許每個(gè)項(xiàng)目以無縫的方式指定和使用其首選的包管理器,從而解決了這個(gè)問題。

此外,Corepack 在你的項(xiàng)目和全局系統(tǒng)之間提供一層隔離,確保即使全局包升級或刪除,你的項(xiàng)目也能正常運(yùn)行,這提高了項(xiàng)目的一致性和可靠性。

安裝和使用 Corepack

安裝 Corepack 非常簡單。由于它從版本 16 開始與 Node.js 捆綁在一起,因此你只需安裝或升級 Node.js 到這個(gè)或更高版本即可。

安裝后,你可以在 package.json 文件中為項(xiàng)目指定包管理器,如下所示:

{
  "packageManager": "yarn@2.4.1"
}

然后,你可以在項(xiàng)目中啟用 Corepack,如下所示:

corepack enable

如果你在項(xiàng)目目錄下的終端中輸入 yarn,即便當(dāng)前沒有安裝 Yarn,Corepack 會(huì)自動(dòng)檢測并安裝正確版本的。

這種做法會(huì)確保始終使用 Yarn v2.4.1 版本來安裝項(xiàng)目的依賴項(xiàng),無論系統(tǒng)上安裝的全局 Yarn 版本如何。

如果你想全局安裝 Yarn 或使用特定版本,您可以運(yùn)行:

corepack install --global yarn@stable

Corepack 仍然是一個(gè)實(shí)驗(yàn)性功能

盡管在 Node.js 16 中引入了 Corepack,但它仍然被標(biāo)記為實(shí)驗(yàn)性的。這表示雖然它現(xiàn)在設(shè)計(jì)并運(yùn)行的良好,但仍在積極開發(fā)中,并且其行為的某些方面將來可能會(huì)發(fā)生變化。

總之,Corepack 易于安裝、使用簡單,能為你的項(xiàng)目提供額外的可靠性,這絕對是一個(gè)值得探索并融入到你的開發(fā)工作流程中的功能。

Node.js .env 加載器

應(yīng)用程序的配置至關(guān)重要,作為 Node.js 開發(fā)人員,通常會(huì)面臨管理 API 憑據(jù)、服務(wù)器端口號或數(shù)據(jù)庫等配置的需求。

而我們需要一種方法,能在不改變源代碼的情況下,為不同的環(huán)境提供不同的設(shè)置。在 Node.js 應(yīng)用程序中實(shí)現(xiàn)這個(gè)目的的一種流行方法是使用存儲(chǔ)在 .env 文件中的環(huán)境變量。

npm 包 dotenv

在 Node.js 引入對加載 .env 文件的原生支持之前,開發(fā)人員主要使用 dotenv npm 包。dotenv 包將環(huán)境變量從 .env 文件加載到 process.env 中,然后在整個(gè)應(yīng)用程序中都可訪問了。

以下是 dotenv 包的典型用法:

require('dotenv').config();

console.log(process.env.MY_VARIABLE);

很有用,但需要向你的項(xiàng)目添加額外的依賴項(xiàng)。而在通過引入原生 .env 加載器后,你現(xiàn)在可以直接加載環(huán)境變量,而無需任何外部包。

使用原生 .env 加載器

從 Node.js 20 開始,內(nèi)置了從 .env 文件加載環(huán)境變量的功能。這個(gè)功能正在積極開發(fā)中,但對開發(fā)人員來說已經(jīng)游戲改變者了。

要加載 .env 文件,我們可以在啟動(dòng) Node.js 應(yīng)用程序時(shí)使用 --env-file CLI 標(biāo)志。該標(biāo)志指定要加載的 .env 文件的路徑。

node --env-file=./.env index.js

這會(huì)將環(huán)境變量從指定的 .env 文件加載到 process.env 中。然后,這些變量就可以像以前一樣在你的應(yīng)用程序中那樣使用了。

加載多個(gè) .env 文件

Node.js .env 加載器還支持加載多個(gè) .env 文件。當(dāng)你針對不同環(huán)境(例如開發(fā)、測試、生產(chǎn))有不同的環(huán)境變量集時(shí),這非常有用。

你可以指定多個(gè) --env-file 標(biāo)志來加載多個(gè)文件。文件按照指定的順序加載,后面文件中的變量會(huì)覆蓋前面文件中的變量。

node --env-file=./.env.default --env-file=./.env.development index.js

在此示例中, ./.env.default 包含默認(rèn)變量, ./.env.development 包含特定于開發(fā)環(huán)境的變量。 ./.env.development、./.env.default 中同時(shí)存在的變量,前者都將覆蓋后者中的變量。

Node.js 中對加載 .env 文件的原生支持對于 Node.js 開發(fā)人員來說是一項(xiàng)重大改進(jìn),它簡化了配置管理并消除了對額外軟件包的需要。

開始在 Node.js 應(yīng)用程序中使用 --env-file CLI 標(biāo)志并親身體驗(yàn)便利吧!

Node.js import.meta 支持 __dirname 和 __file

__dirname 和 __file 其實(shí)是 CommonJS 模塊中提供的變量,通常用來作為獲取當(dāng)前文件的目錄名稱和文件路徑的方式。然而,直到最近,這些在 ESM 上還不容易獲得,N你必須使用以下代碼來提取 __dirname:

import url from 'url'
import path from 'path'
const dirname = path.dirname(url.fileURLToPath(import.meta.url))

或者,如果你是 Matteo Collina 的粉絲,您可能已經(jīng)選擇使用 Matteo 的 desm[4] npm 包。

Node.js 不斷發(fā)展,為開發(fā)人員提供更有效的方法來處理文件和路徑操作。Node.js v20.11.0 和 Node.js v21.2.0 中引入了一項(xiàng)對 Node.js 開發(fā)人員的重大更改,即內(nèi)置了對 import.meta.dirname 和 import.meta.filename 的支持。

使用 Node.js import.meta.filename 和 import.meta.dirname

值得慶幸的是,隨著 import.meta.filename 和 import.meta.dirname 的引入,這個(gè)過程變得更加容易,讓我們看一下使用新功能加載配置文件的示例。

假設(shè)你需要加載的 JavaScript 文件所在目錄中有一個(gè) YAML 配置文件,你可以這樣做:

import fs from 'fs';

const { dirname: __dirname, filename: __filename } = import.meta;
const projectSetup = fs.readFileSync(`${__dirname}/setup.yml`, "utf8");

console.log(projectSetup);

在這個(gè)例子中,我們使用 import.meta.dirname 來獲取當(dāng)前文件的目錄名,并將其分配給 __dirname 變量,以便符合 CommonJS 的命名約定。

Node.js 原生計(jì)時(shí)器 Promise

我們都知道,Node.js 是一種基于 Chrome V8 JavaScript 引擎構(gòu)建的流行 JavaScript 運(yùn)行時(shí),始終致力于通過不斷的更新和新功能讓開發(fā)人員的生活變得更加輕松。

盡管 Node.js 在 Node.js v15 中就引入了原生計(jì)時(shí)器 Promise 的支持,但我承認(rèn)我并沒有經(jīng)常使用它們。

JavaScript 計(jì)時(shí)器的簡要回顧

在深入探討本機(jī)計(jì)時(shí)器 Promise 之前,我們來簡要回顧一下 JavaScript setTimeout() 和 setInterval() 計(jì)時(shí)器。

setTimeout() API 是一個(gè) JavaScript 函數(shù),一旦計(jì)時(shí)器到期,它就會(huì)執(zhí)行函數(shù)或指定的代碼段。

setTimeout(function(){ 
    console.log("Hello World!"); 
}, 3000);

在上面的代碼中,“Hello World!”將在 3 秒(3000 毫秒)后打印到控制臺(tái)。

另一方面,setInterval() 則用于重復(fù)執(zhí)行指定的函數(shù),每次調(diào)用之間間隔指定延遲。

setInterval(function(){ 
    console.log("Hello again!"); 
}, 2000);

在上面的代碼中,“Hello again!”每 2 秒(2000 毫秒)就會(huì)打印到控制臺(tái)。

舊方法:用 Promise 包裝 setTimeout()

在過去,開發(fā)人員通常必須人為地包裝 setTimeout() 函數(shù),并通過 Promise 使用它,這樣做是為了將 setTimeout() 和 async/await 一起使用。

下面是一個(gè)封裝的示例:

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..."。

這么做沒問題,卻給代碼增加了不必要的復(fù)雜性。

Node.js Native 計(jì)時(shí)器 Promise

使用 Node.js Native 計(jì)時(shí)器 Promise,我們不再需要將 setTimeout() 包裝在 Promise 中。相反,我們可以直接將 setTimeout() 與 async/await 一起使用。

以下是如何使用 Node.js 原生計(jì)時(shí)器 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 導(dǎo)入的。然后我們直接將其與 async/await 一起使用。它將打印"Taking a break...",等待兩秒鐘,然后打印 "Two seconds later..."。

這極大地簡化了異步編程,并使代碼更易于閱讀、編寫和維護(hù)。

Node.js 權(quán)限模型

目前加入 Node.js TSC 的 Rafael Gonzaga 恢復(fù)了 Node.js 權(quán)限模塊的開發(fā)工作,這個(gè)模塊與 Deno 類似,提供了一組進(jìn)程級的可配置資源約束。

出于安全和合規(guī)性原因,管理和控制 Node.js 應(yīng)用程序可以訪問的資源變得越來越重要。因?yàn)橛袝r(shí)候你也不知道項(xiàng)目使用的 npm 包突然被黑了,或是誤下載了一個(gè)惡意 npm 包等情況的發(fā)生。

在這方面,Node.js 引入了一項(xiàng)稱為權(quán)限模塊的實(shí)驗(yàn)性功能,用于管理 Node.js 應(yīng)用程序中的資源權(quán)限。使用 --experimental-permission 命令行標(biāo)志可以啟用這個(gè)功能。

Node.js 資源權(quán)限模型

Node.js 中的權(quán)限模型提供了一套抽象來管理對各種資源(如文件系統(tǒng)、網(wǎng)絡(luò)、環(huán)境變量和工作線程等)的訪問。當(dāng)你想要限制應(yīng)用程序的某個(gè)部分可以訪問的資源時(shí),這個(gè)功能就很有用。

你可以使用權(quán)限模型的常見資源約束,包括:

  1. 使用 --allow-fs-read=* 和 --allow-fs-write=* 進(jìn)行文件系統(tǒng)讀寫,可以指定目錄和特定文件路徑,還可以通過重復(fù)標(biāo)志來限定多種資源
  2. 使用 --allow-child-process 進(jìn)行子進(jìn)程調(diào)用
  3. 使用 --allow-worker 進(jìn)行工作線程調(diào)用

Node.js 權(quán)限模型還通過 process.permission.has(resource, value) 提供運(yùn)行時(shí) API,以允許查詢特定訪問權(quán)限。

如果你嘗試訪問不能允許的資源,例如讀取 .env 文件,就會(huì)看到 ERR_ACCESS_DENIED 錯(cuò)誤:

> 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 權(quán)限模型示例

考慮這樣一個(gè)場,有一個(gè)處理文件上傳的 Node.js 應(yīng)用程序。你希望限制應(yīng)用程序的這一部分,以便它只能訪問存儲(chǔ)上傳文件的特定目錄。

使用 --experimental-permission 標(biāo)志啟動(dòng) Node.js 應(yīng)用程序時(shí)啟用實(shí)驗(yàn)性權(quán)限功能。

node --experimental-permission ./app.js

我們還想專門允許應(yīng)用程序讀取 2 個(gè)受信任的文件 .env 和 setup.yml ,因此我們需要將上面的內(nèi)容更新為:

node --experimental-permission --allow-fs-write=/tmp/uploads --allow-fs-read=.env --allow-fs-read=setup.yml ./app.js

這樣一來,如果應(yīng)用程序?qū)μ峁┑纳蟼髀窂街獾奈募M(jìn)行寫入,就會(huì)因報(bào)錯(cuò)而終止。

請參閱以下代碼示例,了解如何通過 try/catch 包裝資源訪問的地方,或通過權(quán)限檢 API 來進(jìn)行防御性編程,確保不會(huì)因?yàn)闄?quán)限模型的引入導(dǎo)致出現(xiàn)的異常意外終止程序。

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 中的權(quán)限功能仍處于實(shí)驗(yàn)階段,未來可能會(huì)發(fā)生變化。

Node.js 策略模塊

Node.js 策略模塊是一項(xiàng)安全功能,旨在防止惡意代碼在 Node.js 應(yīng)用程序中加載和執(zhí)行。雖然它不會(huì)追蹤加載代碼的來源,但它提供了針對潛在威脅的可靠防御機(jī)制。

策略模塊利用 --experimental-policy CLI 標(biāo)志來啟用基于策略的代碼加載。此標(biāo)志采用策略清單文件(JSON 格式)作為參數(shù)。例如, --experimental-policy=policy.json。

策略清單文件包含 Node.js 在加載模塊時(shí)遵循的策略,這提供了一種強(qiáng)大的方法來控制加載到應(yīng)用程序中的代碼的性質(zhì)。

使用 Node.js 策略模塊

讓我們通過一個(gè)簡單的示例來演示如何使用 Node.js 策略模塊:

  1. 創(chuàng)建策略文件。這個(gè)文件應(yīng)該是一個(gè) JSON 文件,指定應(yīng)用程序加載模塊的策略。我們稱之為 policy.json。

類似:

{
  "resources": {
    "./moduleA.js": {
      "integrity": "sha384-xxxxx"
    },
    "./moduleB.js": {
      "integrity": "sha384-yyyyy"
    }
  }
}

這個(gè)策略文件指定 moduleA.js 和 moduleB.js 應(yīng)加載特定的 integrity 值。

然而,為所有直接和間接依賴項(xiàng)生成策略文件并不簡單。幾年前,Bradley Meck 創(chuàng)建了 node-policy npm 包,它提供了一個(gè) CLI 來自動(dòng)生成策略文件。

  1. 使用 --experimental-policy 標(biāo)志運(yùn)行 Node.js 應(yīng)用程序
node --experimental-policy=policy.json app.js

這個(gè)命令告訴 Node.js 在加載 app.js 中的模塊時(shí)遵守 policy.json 中指定的策略。

  1. 為了防止篡改策略文件,你可以使用 --policy-integrity 標(biāo)志為策略文件本身提供 integrity 值:
node --experimental-policy=policy.json --policy-integrity="sha384-zzzzz" app.js

這個(gè)命令可確保保持策略文件的完整性,即使磁盤上的文件發(fā)生更改也是如此。

integrity 策略的注意事項(xiàng)

Node.js 運(yùn)行時(shí)沒有內(nèi)置功能來生成或管理策略文件,并且可能會(huì)帶來困難,例如根據(jù)生產(chǎn)環(huán)境與開發(fā)環(huán)境以及動(dòng)態(tài)模塊導(dǎo)入來管理不同的策略。

另一個(gè)需要注意的是,如果你已經(jīng)擁有處于當(dāng)前狀態(tài)的惡意 npm 包,那么生成模塊完整性策略文件就為時(shí)已晚。

我個(gè)人建議你可以持續(xù)關(guān)注這方面的更新,并慢慢嘗試逐步采用此功能。

有關(guān) Node.js 策略模塊的更多信息,你可以查看有關(guān)向 Node.js 引入實(shí)驗(yàn)性完整性策略[5]這篇文章,其中提供了有關(guān)使用 Node.js 策略完整性的更詳細(xì)的詳細(xì)教程。

總結(jié)

當(dāng)我們?yōu)g覽了你應(yīng)該在 2024 年開始使用的現(xiàn)代 Node.js 運(yùn)行時(shí)功能時(shí),很明顯,這些功能幫助你簡化開發(fā)流程、增強(qiáng)應(yīng)用程序性能并加強(qiáng)安全性。這些功能不僅僅是時(shí)尚,也重新定義了 Node.js 開發(fā)方式的巨大潛力。

參考資料

[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 引入實(shí)驗(yàn)性完整性策略: 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。翻譯時(shí)有刪改。

責(zé)任編輯:武曉燕 來源: 寫代碼的寶哥
相關(guān)推薦

2023-04-02 14:09:51

2017-06-15 16:44:09

Node.js框架Web 應(yīng)用

2024-02-04 00:00:00

Vue.jsUI 庫開發(fā)

2013-12-20 09:59:53

WebNode.js

2021-03-03 06:39:05

Nodejs前端開發(fā)

2023-11-29 15:01:40

2024-01-09 15:05:16

2023-10-27 11:48:16

云計(jì)算量子計(jì)算

2024-08-13 15:11:57

2020-05-29 15:33:28

Node.js框架JavaScript

2010-08-03 13:20:53

FlexBuilder

2022-05-23 10:26:50

Node.jsJavaScrip

2023-11-14 11:47:34

2024-11-18 14:53:41

2024-07-09 20:58:00

GenAI數(shù)字營銷

2024-08-23 11:55:49

2022-08-16 14:27:56

Java開發(fā)編程

2022-09-04 15:54:10

Node.jsAPI技巧

2024-02-27 09:27:00

JavaScriptNode.jsNPM包

2024-09-03 14:51:11

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號