コードの大規模化にそなえて。
成果物
モジュール化とは
モジュール化とは、別ファイルに分割することである。
問題
モジュール化しないとコードの管理が大変。とくにコードの規模が大きくなるとバグの原因にもなりうる。
規格
Node.jsでのモジュール化規格を含めると4つある。今回はブラウザで使える方法に限る。
歴史
現在はモジュール化できる言語仕様がある。import
, export
文だ。昔はなかった。以下のような段階を経て、コード分離してきた。
方法
- 0. HTML内
- 1. 外部ファイル化
- 2. 順序呼出
- 3. DOMでscriptタグ追加
- 4. require.js
- 5. ES Module
0. HTML内
index.html
<html> <head> <script>alert("JavaScript!!");</script> </head> </html>
モジュール化されていない状態。HTML内にJSが混在している。
1. 外部ファイル化
index.html
<html> <head> <script src="main.js"></script> </head> </html>
main.js
alert("JavaScript!!");
HTMLとJSを分離してスッキリ。でも……
JSからJSを呼び出せない
JSコードの規模が大きくなると、JSファイルを複数に分割したくなる。なのにJSには外部呼出する言語仕様がない。(現在はimport
文がある)
index.html
<html> <head> <script src="main.js"></script> </head> </html>
main.js
class Human { constractor(name=null) { this._name = name; } Speak() { alert(this._name); } } const h = new Human("NAME"); h.Speak();
クラス定義と呼出を別ファイルに分離したいのに……。
2. 順序呼出
JSのファイル分割はできる。ただ、苦肉の策である。HTMLの<script>
タグでJSコードを順に呼び出すことになる。
index.html
<html> <head> <script src="Human.js"></script> <script src="main.js"></script> </head> </html>
main.js
const h = new Human("NAME"); h.Speak();
Human.js
class Human { constractor(name=null) { this._name = name; } Speak() { alert(this._name); } }
依存順序を把握せねばならない
呼出の順序が重要である。main.jsではHuman
を使用しているので、main.jsをロードする前にHuman
を定義してあるHuman.jsをロードせねばならない。さもなくば参照エラーになる。
<script src="Human.js"></script> <script src="main.js"></script>
もしJSコードの依存順序を変更することになれば、それに伴いHTMLの<script>
タグ順序も変更せねばならない。
開発者はJSコード全体の依存順序をすべて把握している必要がある。そして依存関係に変更があれば随時HTMLを更新せねばならない。これは規模が大きくなると非常に苦痛である。
HTMLを変更せねばならない
JSのファイル名やパスが変更されるとsrc
属性値を変更せねばならない。
JSの変更なのにHTMLファイルを編集するのだ。リビジョン管理するときの対象ファイルにHTMLが入ってしまう。
3. DOMでscriptタグ追加
JSコード内で<script>
タグを追加する。
ロードもJSで書けるので、HTMLファイルを修正する必要は最小限で済む。ただ、依存する順序に沿ってロードせねばならない問題は残る。
index.html
<html> <head> <script src="jsloader.js"></script> </head> </html>
jsloader.js
function Load(path) { var script=document.createElement('script'); script.setAttribute("src", path); document.body.appendChild(script); } Load("Human.js"); Load("main.js");
4. require.js
require.jsというライブラリを使う。依存順序を気にせず書ける!
ただ、ライブラリ固有の表現や設定をせねばならない。
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>require.js</title> <script data-main="app.js" src="js/lib/require/require.min.js"></script> </head> <body></body> </html>
app.js
requirejs(['js/app/main.js']);
他のライブラリを使うときは以下のようにrequire.config({...});
を設定する。
app.js
require.config({ paths: { 'jquery': 'js/lib/jquery/jquery-3.3.1.min', } }); requirejs(['js/app/main.js']);
js/app/main.js
define(function(require, exports, module) { const $ = require('jquery'); const Human = require('js/app/Human'); const h = new Human("NAME"); h.Speak(); });
js/app/Human.js
define(function() { return class Human { constractor(name=null) { this._name = name; } Speak() { alert(this._name); } }; });
5. ES Module
ES6からimport
とexport
文が言語仕様に加わる。
ついにJavaScriptは標準でモジュール化できるようになった!
しかし、使える環境は限られる。※
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>ES Module</title> <script src="main.js"></script> </head> </html>
js/app/main.js
import Human from "js/app/Human"; const h = new Human("NAME"); h.Speak();
js/app/Human.js
export default class Human { constractor(name=null) { this._name = name; } Speak() { alert(this._name); } };