JavaScript | ES6+ 文法:モジュール – 再エクスポート

JavaScript JavaScript
スポンサーリンク

再エクスポートとは何か(まずイメージから)

再エクスポート(re-export)は、
「他のモジュールから import したものを、そのまま(または名前を変えて)もう一度 export し直すこと」 です。

簡単に言うと:

  • A ファイルで何かを定義する
  • B ファイルで A から import して、それをそのまま export(再エクスポート)する
  • C ファイルは、A ではなく B からまとめて import できる

という流れです。

// math/add.js
export function add(a, b) {
  return a + b;
}
JavaScript
// math/index.js
export { add } from "./add.js";  // 再エクスポート
JavaScript
// main.js
import { add } from "./math/index.js"; // add.js を意識せず使える
JavaScript

ここが重要です。
再エクスポートは、「内部構造は細かくファイル分けしつつ、外に見せる入口はシンプルにする」ための仕組みです。
大きくなってきたプロジェクトほど、これが効いてきます。

基本形:export { 名前 } from “モジュール”

特定のものだけを再エクスポートする

一番シンプルな再エクスポートは、この形です。

export { 名前 } from "./path.js";
JavaScript

具体例を見ます。

// math/add.js
export function add(a, b) {
  return a + b;
}
JavaScript
// math/sub.js
export function sub(a, b) {
  return a - b;
}
JavaScript
// math/index.js
export { add } from "./add.js";  // add を再エクスポート
export { sub } from "./sub.js";  // sub を再エクスポート
JavaScript
// main.js
import { add, sub } from "./math/index.js";

console.log(add(2, 3)); // 5
console.log(sub(5, 1)); // 4
JavaScript

main.js から見ると、add.jssub.js の存在を知らなくても、
math/index.js だけ見ておけば必要なものが揃う、という状態になっています。

ここが重要です。
「内部の細かいファイル名は隠し、math/index.js を窓口にする」
これが再エクスポートの基本的な使い方です。

別名をつけて再エクスポートする(as)

再エクスポート時に名前を変えることもできます。

// math/add.js
export function add(a, b) {
  return a + b;
}
JavaScript
// math/index.js
export { add as plus } from "./add.js";
JavaScript
// main.js
import { plus } from "./math/index.js";

console.log(plus(2, 3)); // 5
JavaScript

元のモジュールでは add という名前でも、
外に見せる名前を plus に変えられます。

まとめて再エクスポート:export * from “モジュール”

「そのモジュールの名前付き export を全部まとめて外に流す」

export * from "./x.js" は、
「そのモジュールの名前付き export を全部、このモジュールからも export する」 という意味です。

// math/basic.js
export function add(a, b) { return a + b; }
export function sub(a, b) { return a - b; }
JavaScript
// math/advanced.js
export function mul(a, b) { return a * b; }
export function div(a, b) { return a / b; }
JavaScript
// math/index.js
export * from "./basic.js";
export * from "./advanced.js";
JavaScript
// main.js
import { add, sub, mul, div } from "./math/index.js";

console.log(add(1, 2), mul(3, 4));
JavaScript

index.js が「basic と advanced から export されたものを全部まとめて再エクスポートする」役目です。

ここが重要です。
export * は「名前付き export だけが対象」です。
default export は含まれません(後で触れます)。

名前がかぶるときの注意

もし複数のモジュールから export * していて、
同じ名前の export が存在すると、どれが正しいか分からずエラーになります。

// a.js
export function hello() {}
JavaScript
// b.js
export function hello() {}
JavaScript
// index.js
export * from "./a.js";
export * from "./b.js"; // hello が重複 → エラー
JavaScript

こういうときは、export * ではなく、
「どの名前を再エクスポートするか」を厳選したほうが安全です。

default export を再エクスポートする

default をそのまま流し直す

default export も再エクスポートできます。
形が少し違います。

export { default } from "./Module.js";
JavaScript

例を見てみます。

// User.js
export default class User {
  constructor(name) {
    this.name = name;
  }
}
JavaScript
// index.js
export { default as User } from "./User.js";
JavaScript
// main.js
import { User } from "./index.js";

const u = new User("Alice");
JavaScript

ポイントは次の通りです。

  • default 自体を名前付きで再エクスポートするときは default as 名前 と書く
  • こうすると、「index.js の名前付き export として User が使える」ようになる

default をそのまま default として流したいときはこうも書けます。

// index.js
export { default } from "./User.js";
JavaScript

この場合、index.js の default export になります。

// main.js
import User from "./index.js";
JavaScript

再エクスポートを使う場面と設計の考え方

「バレル(barrel)モジュール」で入口を一本化する

現場では、index.js などの「まとめ役」モジュールを作って、
そこから再エクスポートするパターンがよく使われます。
これを「バレル(barrel)モジュール」と呼ぶことがあります。

例えば、models ディレクトリがあったとします。

models/
  User.js
  Post.js
  Comment.js
  index.js
// models/User.js
export default class User { /* ... */ }

// models/Post.js
export default class Post { /* ... */ }

// models/Comment.js
export default class Comment { /* ... */ }
JavaScript
// models/index.js
export { default as User } from "./User.js";
export { default as Post } from "./Post.js";
export { default as Comment } from "./Comment.js";
JavaScript

こうしておくと、外からはこう書けます。

// main.js
import { User, Post, Comment } from "./models/index.js";
// あるいは "./models" だけでいける設定も多い
JavaScript

ここが重要です。
再エクスポートを使って「まとめ役」を作ると、
他のファイルは「細かいファイル構成」を知らなくてもよくなります。
この「依存先をシンプルにする」のが、再エクスポートの最大のメリットです。

内部構造を変えても外の import を壊さない

例えば、最初はこんな構成だったとします。

// math.js
export function add(a, b) { /* ... */ }
export function sub(a, b) { /* ... */ }
JavaScript

他のファイルではこう import していました。

import { add, sub } from "./math.js";
JavaScript

後から、「中身をファイル分割したい」と思ったとします。

math/
  add.js
  sub.js
  index.js
// math/add.js
export function add(a, b) { /* ... */ }

// math/sub.js
export function sub(a, b) { /* ... */ }

// math/index.js
export { add } from "./add.js";
export { sub } from "./sub.js";
JavaScript

これでも、外の import はそのままにできます。

// main.js
import { add, sub } from "./math/index.js"; 
// 設定によっては "./math" だけで済む
JavaScript

外側のコードから見れば「math モジュール」が提供している add / sub に変わりはないので、
内部をどう分割しようが関係ありません。

再エクスポートは、
「内側の構造を自由に変えられる余地を残す」
という設計上の自由度をくれます。

実践的な再エクスポートの書き方パターン

複数モジュールの名前付き export を集約する

// utils/string.js
export function capitalize(str) { /* ... */ }
export function trimAll(str) { /* ... */ }

// utils/number.js
export function clamp(value, min, max) { /* ... */ }
export function randomInt(min, max) { /* ... */ }
JavaScript
// utils/index.js
export * from "./string.js";
export * from "./number.js";
JavaScript
// main.js
import { capitalize, randomInt } from "./utils/index.js";
JavaScript

この形だと、utils/index.js を開くだけで、
「どのユーティリティが外に公開されているか」が一発で分かります。

default + 名前付きを混ぜて再エクスポートする

// api/request.js
export default function request(url, options) { /* ... */ }

// api/helpers.js
export function buildUrl(path) { /* ... */ }
export function parseResponse(res) { /* ... */ }
JavaScript
// api/index.js
export { default as request } from "./request.js";
export * from "./helpers.js";
JavaScript
// main.js
import { request, buildUrl } from "./api/index.js";
JavaScript

request は default を名前付きとして再エクスポート、
buildUrl などは export * でまとめて流している例です。

つまずきやすいポイントと注意

「再エクスポート = import してから export」ではない書き方

もちろん、こう書いても再エクスポートに近いことはできます。

// index.js
import { add } from "./add.js";
export { add };
JavaScript

ただ、ES モジュールではこれを一気に書いた形が export { add } from "./add.js"; です。
手順としては同じですが、後者の方が「これは再エクスポートだ」と一目で伝わります。

できるだけ、「from を伴う export は再エクスポート」と体で覚えておくと読みやすいです。

default は export * には含まれない

さきほど少し触れましたが、もう一度強調します。

// someModule.js
export default function main() {}
export function helper() {}
JavaScript
// index.js
export * from "./someModule.js";
JavaScript

このとき、再エクスポートされるのは helper だけです。
main(default)は流れてきません。

default も出したいなら、明示的に書きます。

export { default as main } from "./someModule.js";
JavaScript

まとめ

再エクスポートの核心は、
「モジュールの中身(他モジュールの export)を、そのまま(または名前を変えて)外に流すことで、“入口”を整理する」 ことです。

押さえておきたいポイントをまとめると:

  • export { 名前 } from "./x.js"; で特定の export を再エクスポートできる
  • export * from "./x.js"; で、そのモジュールの名前付き export を全部再エクスポートできる
  • export { default as 名 } from "./x.js"; で default を名前付きとして外に出せる
  • index.js や “barrel” モジュールを作って、再エクスポートで入口を一本化すると、import 側がスッキリする
  • 内部のファイル構成を変えても、再エクスポートの窓口さえ保てば外のコードを壊さずに済む

最初は小さなディレクトリ(例えば models/utils/)を作って、
その中にいくつかファイルを分け、index.js で再エクスポートしてみてください。
「中は細かく、外からはシンプルに」というモジュール設計の気持ちよさが、感覚で分かってくるはずです。

タイトルとURLをコピーしました