mcpField
M
module-pattern

module-pattern

github.com/patternsdev開発257632 回インストール28 週間前に更新
リポジトリ

module-pattern について

拡大するコードベースを整理する JavaScript 開発者向けのモジュールガイドです。非公開値のカプセル化、明示的なインポートとエクスポートによる機能共有、必要に応じた読み込みを解説します。

主な機能

  1. 01保守しやすいモジュールへの分割
  2. 02非エクスポート値によるカプセル化
  3. 03名前付き・デフォルトエクスポート
  4. 04動的インポートによる読み込み
  5. 05グローバルな名前の衝突を抑制

活用シーン

  1. 01大規模なコードベースをファイルに分割
  2. 02コンポーネントのスタイルや内部値を隠す
  3. 03初期表示に不要な機能を必要時に読み込む

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/patternsdev/skills --skill module-pattern

インストール後、Claude Code で/module-patternを入力して読み込まれたか確認します。

詳細

モジュールパターン

目次

アプリケーションやコードベースが大きくなるにつれて、コードを保守しやすく、適切に分割しておくことがますます重要になります。モジュールパターンを使うと、コードを再利用可能な小さな単位に分割できます。

コードを再利用可能な小さな単位に分割できるだけでなく、モジュールを使うと、特定の値をファイル内で プライベート に保てます。デフォルトでは、モジュール内の宣言はそのモジュール内にスコープが限定され(カプセル化され)ます。特定の値を明示的にエクスポートしない限り、その値はモジュールの外部から利用できません。これにより、コードベースのほかの部分で宣言された値との名前の衝突を防ぎやすくなります。値がグローバルスコープで利用可能にならないためです。

使用する場面

  • コードを保守しやすく、カプセル化された単位に整理する必要がある場合に使用します
  • 特定の値をモジュール内でプライベートに保ち、グローバルスコープの汚染を避けたい場合に役立ちます
  • ツリーシェイキングを有効にし、バンドルサイズを削減するために使用します

使用しない場面

  • ES2015 のネイティブモジュールで静的な import/export が利用できる場合は、ツールの対応とツリーシェイキングを改善するため、静的インポートを優先します
  • バンドラーをすでに使用しているコードベースで、カプセル化だけを目的として IIFE ベースのモジュールパターンを使用する場合
  • モジュールのオーバーヘッドによって不要な複雑さが生じる、単純なスクリプトの場合

手順

  • モジュールの定義には ES2015 の import/export 構文を使用します
  • 複数の値には名前付きエクスポートを使用し、モジュールの主要な値にはデフォルトエクスポートを使用します
  • エクスポートしない値はプライベートに保ち、名前の衝突リスクを軽減します
  • 初期バンドルサイズを削減するため、必要に応じて動的な import() を使用してモジュールを読み込みます

詳細

ES2015 モジュール

ES2015 では、JavaScript に組み込みのモジュールが導入されました。モジュールは JavaScript コードを含むファイルで、通常のスクリプトとは動作が一部異なります。

数学関数を含む math.js というモジュールの例を見てみましょう。

export function add(x, y) {
  return x + y;
}

export function multiply(x) {
  return x * 2;
}

export function subtract(x, y) {
  return x - y;
}

export function square(x) {
  return x * x;
}

math.js ファイルには、シンプルな数学の処理が含まれています。渡された値を加算、乗算、減算したり、平方を求めたりする関数があります。

math.js の関数をほかのファイルから利用できるようにするには、まずそれらを エクスポート する必要があります。モジュールからコードをエクスポートするには、export キーワードを使います。関数をエクスポートする方法のひとつは、名前付きエクスポート を使うことです。公開したい部分の前に export キーワードを追加するだけです。

その後、import キーワードを使って、別のファイルに値をインポートできます。どのモジュールから関数をインポートするのかを JavaScript に知らせるには、from の値とモジュールへの相対パスを追加する必要があります。

import { add, multiply, subtract, square } from "./math.js";

モジュールの大きな利点は、export キーワードを使って明示的にエクスポートした値_だけにアクセスできる_ことです。export キーワードを使って明示的にエクスポートしていない値は、そのモジュール内でのみ利用できます。

math.js ファイル内でのみ参照できる privateValue という値を作成してみましょう。

const privateValue = "This is a value private to the module!";

export function add(x, y) {
  return x + y;
}

export function multiply(x) {
  return x * 2;
}

export function subtract(x, y) {
  return x - y;
}

export function square(x) {
  return x * x;
}

privateValue の前に export キーワードを追加していないことに注目してください。privateValue 変数をエクスポートしていないため、math.js モジュールの外部からこの値にアクセスすることはできません。

値をモジュール内で非公開にしておくことで、グローバルスコープを誤って汚染するリスクを減らせます。モジュールを使う開発者が作成した値を、非公開の値と同じ名前だったために誤って上書きしてしまうことを心配する必要はありません。これにより、名前の衝突を防げます。

エクスポートの名前がローカルの値と衝突する場合もあります。その場合は、as キーワードを使ってインポートする値の名前を_変更_できます。

import {
  add as addValues,
  multiply as multiplyValues,
  subtract,
  square,
} from "./math.js";

function add(...args) {
  return args.reduce((acc, cur) => cur + acc);
}

function multiply(...args) {
  return args.reduce((acc, cur) => cur * acc);
}

/* From math.js module */
addValues(7, 8);
multiplyValues(8, 9);
subtract(10, 3);
square(3);

/* From index.js file */
add(8, 9, 2, 10);
multiply(8, 9, 2, 10);

名前付きエクスポートのほかに、デフォルトエクスポート も使えます。モジュールごとに設定できるデフォルトエクスポートは1つだけです。

export default function add(x, y) {
  return x + y;
}

export function multiply(x) {
  return x * 2;
}

export function subtract(x, y) {
  return x - y;
}

export function square(x) {
  return x * x;
}

名前付きエクスポートとデフォルトエクスポートの違いは、値をモジュールからエクスポートする方法です。その結果、値をインポートする方法も変わります。

これまでは、名前付きエクスポートをインポートするために波括弧を使う必要がありました:import { module } from 'module'。 デフォルトエクスポートの場合は、波括弧を使わずに値をインポートできます:import module from 'module'。

import add, { multiply, subtract, square } from "./math.js";

add(7, 8);
multiply(8, 9);
subtract(10, 3);
square(3);

JavaScript は、この値が常にデフォルトでエクスポートされた値であると認識するため、インポートしたデフォルト値には、エクスポート時とは別の名前を付けることができます。

アスタリスク * を使い、モジュールをインポートする際の名前を指定することで、名前付きエクスポートとデフォルトエクスポートを含む、モジュールのすべてのエクスポートをインポートすることもできます。

import * as math from "./math.js";

math.default(7, 8);
math.multiply(8, 9);
math.subtract(10, 3);
math.square(3);

この場合、モジュールからすべてのエクスポートをインポートします。不要な値までインポートしてしまう可能性があるため、この方法を使う際は注意してください。

* を使うと、エクスポートされた値だけがすべてインポートされます。モジュール内で非公開の値は、明示的にエクスポートしない限り、そのモジュールをインポートするファイルからは引き続き利用できません。

React

React を使ってアプリケーションを構築する際、多数のコンポーネントを扱うことがよくあります。これらのコンポーネントをすべて 1 つのファイルに記述する代わりに、それぞれを別のファイルに分けて、各コンポーネントのモジュールを作成できます。

コンポーネントを別々のファイルに分けることができます。

  • TodoList.js に List コンポーネント
  • Button.js にカスタマイズした Button コンポーネント
  • Input.js にカスタマイズした Input コンポーネント

アプリ全体では、UI ライブラリからインポートしたデフォルトの Button と Input コンポーネントを使いたくありません。代わりに、それぞれのファイル内の styles オブジェクトで定義したカスタムスタイルを適用し、独自バージョンのコンポーネントを使いたいと考えています。アプリケーション内でデフォルトの Button と Input コンポーネントをその都度インポートして、何度もカスタムスタイルを適用するのではなく、デフォルトの Button と Input コンポーネントを一度だけインポートし、スタイルを適用して、カスタムコンポーネントとしてエクスポートできます。

Button.js と Input.js の両方に style というオブジェクトを定義できる点に注目してください。この値は モジュールスコープ なので、名前の衝突を心配せずに同じ変数名を再利用できます。

動的インポート

ファイルの先頭ですべてのモジュールをインポートすると、ファイル内の残りの処理が実行される前に、すべてのモジュールが読み込まれます。条件に応じてモジュールをインポートするだけでよい場合もあります。動的インポートを使うと、必要に応じてモジュールをインポートできます。

import("module").then((module) => {
  module.default();
  module.namedExport();
});

// Or with async/await
(async () => {
  const module = await import("module");
  module.default();
  module.namedExport();
})();

モジュールを動的にインポートすることで、ページの読み込み時間を短縮できます。ユーザーが実際に必要とするコードだけを、ユーザーが必要とする タイミング で読み込み、解析し、コンパイルすればよいのです。

必要に応じてモジュールをインポートできるだけでなく、import() 関数には式を渡せます。これにより、テンプレートリテラルを渡して、指定した値に基づいてモジュールを動的に読み込めます。

const res = await import(`../assets/dog${num}.png`);

こうすることで、ハードコードされたモジュールパスに依存せずに済みます。ユーザー入力、外部ソースから受け取ったデータ、関数の戻り値などに基づいてモジュールをインポートする方法に柔軟性が加わります。

モジュールパターンを使うと、公開すべきでないコードの一部をカプセル化できます。これにより、意図しない名前の衝突やグローバルスコープの汚染を防げるため、複数の依存関係や名前空間を扱う際のリスクを軽減できます。すべての JavaScript ランタイムで ES2015 モジュールを使用するには、Babel などのトランスパイラーが必要です。

出典

コメント