JavaScriptの新しい仕様には、モジュールという仕組みがあります。現時点では一部のブラウザしか対応していませんが、ECMAScript 2015のModulesの標準仕様として策定されているため、やがてすべてのブラウザで使えるようになっていくでしょう。この機能は、ES2015 Modules、ES6 Modules、ES Modules、ESMなどと呼ばれています(以下、ES Modulesと記載します)。
webpack 2+やRollup.jsなどのモダンなフロントエンドのツールを通して、すでにES Modulesを使っているエンジニアも多いと思います。この記事では、ブラウザネイティブで使えるES Modulesに焦点をあて、ES Modulesの導入で解決できる課題と利点を紹介します。
HTML+JSではモジュールの仕組みがなかった
JavaScript自体には他のJSファイルを取り込む標準的な仕様がいままではありませんでした。従来だと、外部JS(たとえばjQueryのライブラリなど)を読み込みたい時に、HTMLファイルにscript
タグを書き込むことで別ファイルを読み込んでいました。たとえば次のようなコードです。
<script src="js/vender/jquery.min.js"></script>
<script src="js/vender/jquery.cookie.js"></script>
<script src="js/vender/jquery.easing.1.3.js"></script>
<script src="js/common.js"></script>
<script src="js/utils.js"></script>
<script src="js/app.js"></script>
この方法だと扱いづらい点があります。
- HTMLファイルと密結合となる
- JSファイル単独で管理できない
- 読み込みの順番を気にしなければならない
これを解決するための手段として、さまざまなアプローチが登場しました。有名なところだと、AMD(+RequireJS)やCommonJS(+Browserify)などの技術があります。これらは独自仕様だったため、これからはES Modulesが標準仕様として取って代わっていくと考えられます。
サンプルで理解するES Modules
ES Modulesをブラウザで使うにはいくつか手順があります。サンプルを通して、1つ一つおさえていきましょう。
利用可能なブラウザ
まずは利用できるブラウザを用意ください。デフォルトでES Modulesを使えるのは次のブラウザです。
- iOS Safari 10.1以上 (2017年3月リリース)
- macOS Safari 10.3以上 (2017年3月リリース)
- Chrome 61以上 (2017年8月リリース)
- Edge 16以上 (2017年10月リリース)
- Firefox 60以上 (2018年5月リリース)
▼ES Modulesのブラウザサポート状況。「Can I Use…」より(2018年5月現在)
読み込まれる側の処理
まずはモジュールとしての外部JSファイルを用意しましょう。次のコードはブラウザでアラートを表示するだけのサンプルコードです。
▼sample-alert.js
export function sayMessage(message) {
alert(message);
}
外部ファイルのJSファイルではexport
文を使って定義します。export
で宣言されたものだけが他のJSファイルから参照できます。
読み込む側の処理
HTMLには次のコードを記述します。従来はtype="text/javascript"
と記載していましたが、ES Modulesを使う時はtype="module"
と書きます。module
を指定しないと、import
やexport
などのJSコード内の宣言がエラーとなります。
▼パターン1
<html>
<head>
<meta charset="UTF-8">
<script type="module">
import {sayMessage} from "./sample-alert.js";
sayMessage("こんにちは世界");
</script>
</head>
<body>
</body>
</html>
src
属性で外部ファイルを指定できます。インラインでも外部ファイルでも、どちらでもES Modulesを読み込めます。
▼パターン2
<html>
<head>
<meta charset="UTF-8">
<script type="module" src="index.js"></script>
</head>
<body>
</body>
</html>
import { sayMessage } from "./sample-alert.js";
sayMessage("こんにちは世界");
import
文のfrom
の中では必ず./
や../
、/
、といったパスで記述しなければなりません。xxx.js
というように記述するのはNGで、./xxx.js
とファイルパスを明確に記載します。拡張子も必須です。Node.jsでは拡張子無しの記述ができましたが、ブラウザでは必ず拡張子を記載ください。
これをブラウザで開くとJSファイルで記載されたコードが実行されていることがわかります。
外部JSも扱える
もう1つ興味深いサンプルを紹介しましょう。import
文にはURLも指定できます。ES Modulesに対応した外部JSであれば、CDNから読み込めるので次のように記載できます。
<html>
<head>
<meta charset="UTF-8">
<script type="module">
import * as THREE from 'https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.module.js';
// Three.jsの起動コード (略)
</script>
</head>
<body>
</body>
</html>
次の作例はWebGL用のJSライブラリThree.jsを使ったサンプルです。
注意点として、どのJSライブラリでもES Modulesとして読み込めるわけではありません。Three.jsはES Modulesで設計された数少ないJSライブラリです。有名どころだとjQueryやReact、Vue.jsなどはES Modulesとして配布されていないため、現時点ではブラウザネイティブではES Modulesとして利用できません。
ES Modulesを導入した場合の課題
ES Modulesを採用し、JSファイルを細かく分けた場合、ウェブサーバーから転送すべきファイルの数が増える傾向があります。
みなさんもご存知の通り、HTTP/1.1プロトコルでは同時接続数が限られるため多くのファイルを転送するのが苦手です。ウェブ制作の現場ではCSSスプライトやJSファイルの結合などの手法でファイルを1つに結合し、転送ファイルの数を減らしますよね。
転送ファイルが増えることの解決には、HTTP/2に対応したウェブサーバーへ移行するのが適した手段の1つです。ES ModulesでJSファイルの数が増えても、HTTP/2プロトコルでは支障にならないでしょう。
それどころか、HTTP/2環境下ではウェブサイトの読み込みでメリットがあります。ウェブサイト内の共通JSやベンダーJSはブラウザのキャッシュに任せて(キャッシュヒット率を高めて)、ページ固有のJSファイルだけ転送する形としたとします。そうすると、ページごとに必要な差分JSしか読み込みが発生しなくなるはずです。
モジュールバンドラーとの比較
規模のあるJavaScriptの開発ではモジュールの仕組みは必須です。昨今のフロントエンドの開発ではwebpackやRollupなどのツールを使ってモジュールのJS開発をしている方がほとんどでしょう。
これらのツールは現行ブラウザでもモジュールの仕組みを利用できるように、ES Modulesを使ったコードをES5互換のJSファイルへと変換します。
ES Modulesがブラウザで標準で使えるようになった場合、このワークフローは変わっていくのでしょうか。
モジュールバンドラーは不要になる?
都内の勉強会に参加すると、「ES Modulesが普及すると、バンドルツールは不要となる」といった意見をよく聞きます。その時代にはきっとES2015の全機能はブラウザ標準で使えるようになっているでしょう。わざわざNode.jsでBrowserifyやBabelなどの環境構築をする必要がなくなるかもしれません。ネイティブにそのままブラウザで確認できる利便性は大きな利点です。
モジュールバンドラーが必要な場面はある
ただし、パッケージマネージャーnpmで管理するJSライブラリをバンドルするにはwebpackなどのツールが必要で、JSXやTypeScriptなどのコンパイラも必須なのは変わらないはずです。高度なSPA開発では引き続きこの手のツールは使われるはずです。
ES Modulesが現状のフロントエンドの作り方を破壊するのではなく、組み合わせることでさまざまな課題にアプローチできるようになると考える方が望ましいでしょう。
まとめ
ES ModulesはこれまではJavaScriptの開発スタイルに変化をもたらす大きな機能です。すでにモジュールバンドラーの利用者はES Modulesに親しんできたかもしれませんが、これからはブラウザネイティブで利用できる時代となっていくでしょう。
今回は、ES Modulesの概要を紹介しただけでしたが、続編記事ではES Modulesのさまざまな記述方法・使い方を解説します。この記事で解説したサンプルはGitHubにて公開しています。