最新の JavaScript 開発で ES モジュールを使用したことがあると思いますが、その進化の背後にある歴史をご存知ですか?初期の JavaScript の実践から今日のモジュール システムに至るまでの過程を理解すると、私たちがどこまで到達したか、そしてなぜ ES モジュールがゲームチェンジャーであるかを理解するのに役立ちます。
時は 1995 年、最初の Web ページが作成されてから 4 年後です。ほとんどの Web サイトはシンプルで、テキストと最小限のインタラクティブ性を備えた静的なページでした。しかし、開発者はすぐに Web ページをより動的にする方法を探していました。
この環境では、Netscape (当時の有力な Web ブラウザ) は、ブラウザ内で直接実行するスクリプト言語を作成するために Brendan Aich を雇いました。これが JavaScript の誕生につながりました。JavaScript は、特に Web デザイナーなどの非プログラマーにとって、シンプルでアクセスしやすいように設計された言語です。そして彼はそうし、10 日で最初のバージョンを完成させました。
本来、JavaScript は、サーバーとのデータの往復を必要とせずに、フォーム検証などの小さな機能強化を Web ページに追加することを目的としていました。しかし、Web サイトがよりインタラクティブになるにつれて、JavaScript は当初の目的を超えて急速に成長しました。
初期の頃、すべての JavaScript コードはグローバル スコープ内に存在していました。より多くの開発者が同じページにコードを追加するにつれて、名前の衝突のリスクが増大しました。複数のスクリプトで同じ変数名または関数名を使用すると、コードが予期しない形で壊れる可能性があります。
これを管理するために、開発者は衝突を防ぐための命名規則に頼りました。コードの一部が 1 回だけ実行されることを意図している場合、開発者は多くの場合、それを IIFE (Immediately Invoked Function Expression) でラップします。これにより、関数と変数のスコープが関数内に保たれ、グローバル名前空間が汚染されるのを防ぎました。
(function init() { function getData(){ // ... } })()
ほとんどの Web サイトはクライアント側のロジックをほとんど使用せずにサーバー側でレンダリングされていたため、当時はこれで十分でした。
2008 年に Ryan Dahl は、サーバー側アプリケーションを構築するための JavaScript ランタイムである Node.js を作成しました。これにより、まったく新しい可能性の世界が開かれましたが、モジュール システムがないため、開発者は大規模なコードベースの管理に苦労していました。
2009 年に、サーバー側でこの問題を解決するために CommonJS が導入されました。 CommonJS モジュール システムを使用すると、開発者はモジュールを定義し、機能を公開し、他のモジュールをインポートできます。これがどのように機能したかの例を次に示します:
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
CommonJS では、各ファイルは独自のモジュールとして扱われ、モジュールは require 関数を使用してインポートされ、module.exports を使用してエクスポートされます。
CommonJS の主な機能には次のようなものがあります。
モジュールを必要とする場合、ファイル拡張子はオプションです (例: require('./math') は自動的に math.js を探します)。
モジュールのロードは同期的です。つまり、プログラムはモジュールのロードを待ってから実行を続行します。
記事の後半では、ライアン・ダールがこれら 2 つの設計上の決定を後悔していると認めた理由を説明します。
同じ頃、AMD (Asynchronous Module Definition) と呼ばれる別のモジュール システムが開発されました。 CommonJS は主にサーバー側 JavaScript に焦点を当てていましたが、AMD はブラウザーでクライアント側 JavaScript を処理するように設計されました。
AMD の重要な機能は、モジュールを非同期でロードできる機能でした。これにより、ブラウザーは常にページに必要な JavaScript のみを読み込むことができるようになり、最初のページ読み込み時間が短縮されてパフォーマンスが向上しました。また、依存関係の解決に関連する問題も解決され、依存関係の読み込みが完了した場合にのみモジュールが実行されるようになりました。
AMD の特典には以下が含まれます:
2010 年の npm (サーバーサイド JavaScript のパッケージ マネージャー) の台頭により、ブラウザーとサーバー間でコードを共有する必要性が明らかになりました。 Browserify は、ブラウザの環境と互換性があるようにコードを変換することで、開発者がブラウザで CommonJS モジュールを使用できるようにするツールです。
CommonJS と AMD という 2 つの競合する標準。ビルドステップを必要とせずにどこでも動作できる単一モジュールシステムが必要でした。そして 2011 年には、ユニバーサル モジュール定義 (UMD) が導入されました。
UMD は両方の長所を組み合わせたもので、開発者は以下で実行できるモジュールを作成できます。
UMD は、Lodash、Underscore.js、Backbone.js、Moment.js などの著名なライブラリがそれを支持するライブラリ作成者の間で非常に人気になりました。ただし、UMD にはいくつかの重大な欠点がありました。互換性の問題は解決されましたが、両方のシステムの管理が複雑になり、AMD と CommonJS の両方の問題を引き継ぎました。
2015 年に ES モジュール (ESM) が ECMAScript 標準の一部として導入され、最終的に JavaScript のネイティブ モジュール システムが提供されました。 2017 年までに、すべての主要ブラウザが ES モジュールをサポートし、2020 年には Node.js もサポートを追加しました。
ES モジュールが最適である理由を見てみましょう:
次の UMD コード:
(function init() { function getData(){ // ... } })()
は次のように削減できます:
(function init() { function getData(){ // ... } })()
公平を期すために言うと、実際にそのように UMD を書いた人は誰もいません。彼らは umdify などのツールを使用してそのコードを生成しました。ただし、ES モジュールが組み込まれているため、ビルド手順をスキップしてバンドル サイズを小さくすることができます。
ES モジュールは静的です。つまり、ツールはコンパイル時にコード構造を分析して、どのコードが使用されているか、どのコードが使用されていないかを判断できます。これにより、未使用のコードが最終バンドルから削除されるツリーシェイクが可能になります。
CommonJS および AMD モジュールは動的である (実行時に評価される) ため、これらのシステムではツリー シェークの効率が大幅に低下し、多くの場合バンドルが大きくなります。
CommonJS を使用してモジュールをインポートする場合、ファイル拡張子の指定はオプションです。
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
しかし、数学とは実際には何を指すのでしょうか? JavaScript ファイルですか? JSONファイル? math ディレクトリ内の Index.js ファイル?
ES Lint、typescript、または prettier などの静的分析ツールを使用すると、すべての要件が推測ゲームになります。
math.jsですか?
math.jsx ですか?
math.cjs ですか?
math.mjs ですか?
math.tsですか?
math.tsx ですか?
math.mts ですか?
math.ctsですか?
math/index.js ですか?
math/index.jsx ですか?
おわかりでしょう。
ファイルの読み取りにはコストがかかります。メモリからの読み取りよりもパフォーマンスが大幅に低下します。 math/index.js をインポートすると、IO 操作が 1 つではなく 9 つ行われます。そして、この推測ゲームはツールの速度を低下させ、開発者のエクスペリエンスを損なっています。
ES モジュールでは、ファイル拡張子を必須にすることで、この混乱を回避しています。
モジュールを同期的にロードする (モジュールがロードされるまでプロセス全体をブロックする) CommonJS とは異なり、ES モジュールは非同期です。これにより、モジュールがバックグラウンドで読み込まれている間も JavaScript が実行を継続できるため、特に Node.js などの環境でパフォーマンスが向上します。
明らかな利点にもかかわらず、ES モジュールの導入は簡単な作業ではありませんでした。移行にこれほど時間がかかった理由は次のとおりです:
CommonJS から ES モジュールへの切り替えは、特に大規模なプロジェクトの場合、簡単な変更ではありませんでした。構文の違いとツールのサポートの必要性により、移行は多大な労力を要しました。
node.js が ES モジュールを完全にサポートするまでに 5 年かかりました。この間、開発者は CommonJS (サーバー上) と ES モジュール (ブラウザ上) の両方との互換性を維持する必要がありました。この二重のサポートにより、エコシステム内に多くの摩擦が生じました。
Node.js が ES モジュールのサポートを追加した後でも、CommonJS モジュールは ES モジュールをロードできませんでした。 ES モジュールは CommonJS モジュールをロードできますが、2 つのシステムは完全に相互運用可能ではなく、両方のシステムをサポートする必要があるパッケージ作成者にとってさらなる頭痛の種となっていました。
JavaScript モジュールの将来は明るいです。ここでは、ES モジュールを今後の主要なシステムにする重要な開発をいくつか紹介します。
Node.js 23 では、ついに CommonJS から ES モジュールをロードできるようになりました。
小さな注意点があります: await は非同期関数でのみ使用でき、CommonJS は同期であるため、トップレベルの await を使用する ES モジュールは CommonJS にインポートできません。
npm と競合する新しい JavaScript パッケージ レジストリ。 npm よりも多くの利点がありますが、ここでは説明しません。しかし、興味深いのは、ES モジュール パッケージのアップロードのみが許可されていることです。古い標準をサポートする必要はありません。
グローバル スコープのハックから最新の ES モジュールへの移行により、JavaScript の構造が変化しました。 CommonJS、AMD、UMD の長年の実験を経て、ES モジュールが明確な標準として浮上し、よりシンプルな構文、より優れた最適化、および向上したパフォーマンスを提供します。
ES モジュールへの移行は、特に Node.js のサポートとエコシステムの互換性に関しては困難でしたが、その利点は否定できません。 Node.js 23 により相互運用性が向上し、統合モジュール システムを推進する JSR などの新しいツールにより、ES モジュールが JavaScript のデフォルトになる予定です。
私たちは ES モジュールを採用し続けることで、よりクリーンで高速、より保守しやすいコードを実現し、JavaScript 開発におけるモジュール化の新時代を切り開くことを期待しています。
以上がESモジュールの簡単な歴史の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。