ホームページ > ウェブフロントエンド > jsチュートリアル > Node.js: CJS、バンドラー、ESM の簡単な歴史

Node.js: CJS、バンドラー、ESM の簡単な歴史

Mary-Kate Olsen
リリース: 2024-12-15 05:36:10
オリジナル
389 人が閲覧しました

Node.js: A brief history of cjs, bundlers, and esm

導入

Node.js 開発者であれば、おそらく cjs モジュールと esm モジュールについて聞いたことがあるでしょうが、なぜ 2 つあるのか、Node.js アプリケーションでこれらがどのように共存するのかはよくわからないかもしれません。このブログ投稿では、Node.js の JavaScript モジュールの歴史を簡単に説明します (例付き ?)。これにより、これらの概念をより自信を持って扱うことができます。

グローバルスコープ

当初、JavaScript にはグローバル スコープのみがあり、すべてのメンバーが宣言されていました。 2 つの独立したファイルがメンバーに同じ名前を使用する可能性があるため、コードを共有する場合、これは問題でした。例:

greet-1.js

function greet(name) {
  return `Hello ${name}!`;
}
ログイン後にコピー
ログイン後にコピー

greet-2.js

var greet = "...";
ログイン後にコピー
ログイン後にコピー

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Collision example</title>
  </head>
  <body>
    <!-- After this script, `greet` is a function -->
    <script src="greet-1.js"></script>

    <!-- After this script, `greet` is a string -->
    <script src="greet-2.js"></script>

    <script>
        // TypeError: "greet" is not a function
        greet();
    </script>
  </body>
</html>
ログイン後にコピー
ログイン後にコピー

CommonJS モジュール

Node.js は、CommonJS (cjs とも呼ばれる) を使用した JavaScript モジュールの概念を正式に導入しました。これにより、開発者が何をエクスポートするか (module.exports 経由)、何をインポートするか (require() 経由) を決定できるため、共有グローバル スコープの衝突問題が解決されました。例:

src/greet.js

// this remains "private"
const GREETING_PREFIX = "Hello";

// this will be exported
function greet(name) {
  return `${GREETING_PREFIX} ${name}!`;
}

// `exports` is a shortcut to `module.exports`
exports.greet = greet;
ログイン後にコピー
ログイン後にコピー

src/main.js

// notice the `.js` suffix is missing
const { greet } = require("./greet");

// logs: Hello Alice!
console.log(greet("Alice"));
ログイン後にコピー
ログイン後にコピー

npmパッケージ

Node.js 開発は、開発者が再利用可能な JavaScript コードを公開および利用できるようにした npm パッケージのおかげで人気が爆発的に高まりました。 npm パッケージは、デフォルトでは、node_modules フォルダーにインストールされます。すべての npm パッケージに存在する package.json ファイルは、「main」プロパティを介してどのファイルがエントリ ポイントである Node.js を示すことができるため、特に重要です。例:

node_modules/greeter/package.json

{
  "name": "greeter",
  "main": "./entry-point.js"
  // ...
}
ログイン後にコピー
ログイン後にコピー

node_modules/greeter/entry-point.js

module.exports = {
  greet(name) {
    return `Hello ${name}!`;
  }
};
ログイン後にコピー
ログイン後にコピー

src/main.js

// notice there's no relative path (e.g. `./`)
const { greet } = require("greeter");

// logs: Hello Bob!
console.log(greet("Bob"));
ログイン後にコピー
ログイン後にコピー

バンドラー

npm パッケージは、他の開発者の作業を活用できるため、開発者の生産性が劇的に向上しました。ただし、cjs は Web ブラウザと互換性がないという大きな欠点がありました。この問題を解決するために、バンドラーの概念が生まれました。 browserify は、基本的にエントリ ポイントをトラバースし、require() で処理されたすべてのコードを Web ブラウザと互換性のある単一の .js ファイルに「バンドル」することで機能する最初のバンドラーでした。時間が経つにつれて、追加の機能と差別化要因を備えた他のバンドラーが導入されました。最も注目すべきは、webpack、parcel、rollup、esbuild、vite (時系列順)。

ECMAScript モジュール

Node.js および cjs モジュールが主流になるにつれて、ECMAScript 仕様の管理者はモジュールの概念を含めることを決定しました。これが、ネイティブ JavaScript モジュールが ESModules または esm (ECMAScript モジュールの略) とも呼ばれる理由です。

esm は、メンバーをエクスポートおよびインポートするための新しいキーワードと構文を定義し、デフォルトのエクスポートなどの新しい概念を導入します。時間が経つにつれ、esm モジュールには動的 import() やトップレベルの await などの新しい機能が追加されました。例:

src/greet.js

function greet(name) {
  return `Hello ${name}!`;
}
ログイン後にコピー
ログイン後にコピー

src/part.js

var greet = "...";
ログイン後にコピー
ログイン後にコピー

src/main.js

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Collision example</title>
  </head>
  <body>
    <!-- After this script, `greet` is a function -->
    <script src="greet-1.js"></script>

    <!-- After this script, `greet` is a string -->
    <script src="greet-2.js"></script>

    <script>
        // TypeError: "greet" is not a function
        greet();
    </script>
  </body>
</html>
ログイン後にコピー
ログイン後にコピー

esm は、esm 構文を cjs に変換できるため、バンドラーや TypeScript などの言語のおかげで、開発者に広く採用されるようになりました。

Node.js cjs/esm の相互運用性

需要の高まりにより、Node.js はバージョン 12.x で正式に esm のサポートを追加しました。 cjs との下位互換性は次のように実現されました:

  • Node.js は、package.json が「type」プロパティを「module」に設定しない限り、.js ファイルを cjs モジュールとして解釈します。
  • Node.js は .cjs ファイルを cjs モジュールとして解釈します。
  • Node.js は .mjs ファイルを esm モジュールとして解釈します。

npm パッケージの互換性に関しては、esm モジュールは cjs および esm エントリ ポイントを使用して npm パッケージをインポートできます。ただし、その逆にはいくつかの注意点があります。次の例を見てみましょう:

node_modules/cjs/package.json

// this remains "private"
const GREETING_PREFIX = "Hello";

// this will be exported
function greet(name) {
  return `${GREETING_PREFIX} ${name}!`;
}

// `exports` is a shortcut to `module.exports`
exports.greet = greet;
ログイン後にコピー
ログイン後にコピー

node_modules/cjs/entry.js

// notice the `.js` suffix is missing
const { greet } = require("./greet");

// logs: Hello Alice!
console.log(greet("Alice"));
ログイン後にコピー
ログイン後にコピー

node_modules/esm/package.json

{
  "name": "greeter",
  "main": "./entry-point.js"
  // ...
}
ログイン後にコピー
ログイン後にコピー

node_modules/esm/entry.js

module.exports = {
  greet(name) {
    return `Hello ${name}!`;
  }
};
ログイン後にコピー
ログイン後にコピー

以下は問題なく動作します:

src/main.mjs

// notice there's no relative path (e.g. `./`)
const { greet } = require("greeter");

// logs: Hello Bob!
console.log(greet("Bob"));
ログイン後にコピー
ログイン後にコピー

ただし、次のコマンドは実行できません:

src/main.cjs

// this remains "private"
const GREETING_PREFIX = "Hello";

// this will be exported
export function greet(name) {
  return `${GREETING_PREFIX} ${name}!`;
}
ログイン後にコピー

これが許可されない理由は、esm モジュールではトップレベルの await が許可されるのに対し、require() 関数は同期であるためです。コードは動的 import() を使用するように書き直すこともできますが、Promise を返すため、次のようなものにする必要があります:

src/main.cjs

// default export: new concept
export default function part(name) {
  return `Goodbye ${name}!`;
}
ログイン後にコピー

この互換性の問題を軽減するために、一部の npm パッケージは、条件付きエクスポートで package.json の "exports" プロパティを利用することにより、cjs と mjs の両方のエントリ ポイントを公開します。例:

node_modules/esm/entry.cjs:

// notice the `.js` suffix is required
import part from "./part.js";

// dynamic import: new capability
// top-level await: new capability
const { greet } = await import("./greet.js");

// logs: Hello Alice!
console.log(greet("Alice"));

// logs: Bye Bob!
console.log(part("Bob"));
ログイン後にコピー

node_modules/esm/package.json:

{
  "name": "cjs",
  "main": "./entry.js"
}
ログイン後にコピー

「exports」プロパティをサポートしていない Node.js バージョンとの下位互換性のために、「main」が cjs バージョンをどのように指しているかに注目してください。

結論

cjs および esm モジュールについて知っておく必要があるのは (ほぼ) これだけです (2024 年 12 月時点?)。以下からご意見をお聞かせください!

以上がNode.js: CJS、バンドラー、ESM の簡単な歴史の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート