<script type="module"> とは?
一般的に、 HTML 上で、 JavaScript のコードを記述する際は、 <script> タグを利用すると思います。
コードが長く複雑になると、別ファイルとして指定することが可能です。
ES6(ECMAScript 2015)は、 この方法に加え、新たにモジュール( <script type="module"> ) という指定方法が追加されました。
これは、“モダンな書き方” を支える重要な仕組みのひとつ、ES Modules( import / export ) を使うための仕組みです。
この回では、従来の <script> と何が違うのか、なぜモジュール( <script type="module"> ) が必要なのかを、実際のコード例を交えながら丁寧に見ていきます。
なぜ モジュール( <script type="module"> ) が必要なのか
まず、従来の <script> にはいくつかの問題がありました。
■ グローバルスコープにすべてが置かれる
下記のように、変数がグローバルスコープに置かれる為、別ファイルからも参照が可能です。
a.js
var count = 1;
b.js
console.log(count); // 参照できてしまう
👉 これは、便利な機能である反面、ファイルが増えるほど、変数名の衝突や意図しない上書きが起きやすくなります。
■ ファイル分割が難しい
スクリプトを複数ファイルに分けても、個々のスクリプト間の依存関係を明示できないため、ファイルの読み込み順に強く依存します。
a.js
function add(x, y) {
return x + y;
}
b.js
console.log(add(1, 2));
html
<script src="a.js"></script>
<script src="b.js"></script> <!-- 必ず a.js の後に読み込む必要がある -->
■ 大規模化に向かない
アプリが複雑になるほど、「どのファイルがどの機能を提供しているのか」が分かりにくくなります。
calc.js
function add(x, y) {
return x + y;
}
function sub(x, y) {
return x - y;
}
function mult(x, y) {
return x * y;
}
function div(x, y) {
return x / y;
}
html
<script src="calc.js"></script> <!-- calc.js がどんな関数を提供しているのか、HTML からは判断しづらい -->
<script type="module"> の基本挙動
<script type="module"> を使うと、従来の <script> とは大きく違う挙動になります。
1. モジュールは 自動的に defer(遅延実行)
通常の <script> は HTML の解析を止めて実行されますが、<script type="module"> で読み込まれたモジュールは HTML の解析が終わってから実行されます。
<script type="module" src="main.js"></script>
👉 これは <script defer> と同じ動作です。
2. モジュールは ファイルごとに独立したスコープ
モジュール内で宣言した変数は、グローバル(window)に勝手に定義されません。
// module.js
const x = 1;
console.log(window.x); // undefined
👉 安全で予測しやすいコードになります。
3. モジュールは 1 度だけ実行される(キャッシュされる)
同じモジュールを複数回 import しても、実行されるのは最初の 1 回だけです。
import "./module.js";
import "./module.js"; // 2 回目は実行されない
4. モジュールは 常に strict mode
"use strict" を書かなくても、モジュールは自動的に strict mode で動作します。
strict mode とは、厳格モードのことで、 JavaScript のコードを解釈する際により厳密にチェックを行うモードのことを言います。
strict mode でのチェック例:
- 未宣言変数への代入
- 読み取り専用プロパティへの代入
- 削除不可能なプロパティの削除
- 引数名の重複
- with文の使用
- 八進数リテラル(0123 のような古い書き方)
strict mode 下では、暗黙的に許されていたこれらの動作は、明確にエラーとなります。
👉 strict mode は、バグの早期発見の為、曖昧な記述や潜在的なバグを開発段階で検出する仕組みです。
通常スクリプトとの違い(比較表)
| 特性 | <script> |
<script type="module"> |
|---|---|---|
| スコープ | グローバル | モジュールスコープ |
| 実行タイミング | 即時 | defer と同じ |
| strict mode | 任意 | 常に有効 |
import / export |
使えない | 使える |
| 再実行 | 毎回 | 1 回だけ(キャッシュ) |
this |
window |
undefined |
最小のモジュール例(実際に動くコード)
math.js
export function add(a, b) {
return a + b;
}
main.js
import { add } from "./math.js";
console.log(add(1, 2));
HTML
<script type="module" src="main.js"></script>
👉 これだけでモジュールが成立します。
モジュールと window の関係
モジュールは「安全な箱」です。モジュール内の変数は window に自動で出ません。
const value = 123;
console.log(window.value); // undefined
グローバルに出したい場合は明示的に window への指定が必要です。
window.app = { version: "1.0" };
👉 その為、必要な変数のみを共通化できるので、意図しない変数名の衝突や上書きを防止し易くなります。
CORS とローカルファイルの注意
■ file:// では動かない
モジュールは CORS の制約を受けるため、<script type="module">を定義したHTMLをローカルファイルとして直接開くとエラーになります。
👉 簡易サーバーを使うなど、必ずサーバー経由で呼び出す必要があります。
■ CDN から直接 import できる
import { html } from "https://unpkg.com/lit-html?module";
👉 CDN 等で、モジュールが公開されていれば、従来のような node.js を使ったダウンロード/インストールなく、利用が可能です。
Webクライアントアプリ等のブラウザで動作するフロントエンドとの相性も良いです。
実務での使いどころ
■ 小さなアプリなら、ブラウザだけで十分動かせる
最近のブラウザは ES Modules を標準でサポートしているため、特別なツールを使わなくても <script type="module"> だけでモジュール化した構造化アプリを構成できます。
- 小さな Web アプリ
- Eleventy のような静的サイト
- Web Components を使った UI
- idb などのライブラリを使うアプリ
こうした用途なら、ブラウザだけで完結します。