前提
小規模なJavaScriptのサイトの場合、JQueryを使ってDOMイベントをトリガーにいろんな処理を行ってDOMに反映する。というような処理が多い。このままだとDOMにしか情報を保持していない状態になり、ビューとデータが混在した状態になる。
$(function() {
// イベントハンドラ
$("#button").on("click", function(){
$.ajax({
url: url,
dataType: "json",
}).done(function(results){
// 表示
$.each(results, function(result) {
$('<li>')
.text(result.name)
.appendTo("#list")
.on("click", function(){
//何かしらの処理
});
});
});
});
最初のうちはこれでも大丈夫だが、少し処理を追加していくとわけがわからなくなってくる。そのため、規模が大きくなってくるとビューとデータを分離させて俗にいうMV*の構成にして実装する必要がある。
またunderscore.jsやmoment.jsなど外部のモジュールを使いたいことも多い。
外部のモジュールや自分のモジュールを分割していくと、モジュールの依存関係が発生する。(例:Backbone.jsはunderscore.jsに依存)
それらの依存関係をブラウザの実行時やファイル結合時にうまく解消する必要が出てくる。
従来の解決方法
JavaScriptにはサーバーサイド言語にあるようなモジュールの定義や他のモジュールを読み込む仕組みが言語仕様として用意されていない。そのため、通常は依存関係を考えてscriptタグを順番に並べたり、順番に結合したりする。
ある程度の規模の場合は、concatで必要なJSファイルを結合していって十分な場合は多い。実際、業務アプリケーションをAngularJSで以前作っていたがその時はGruntでconcatしてビルドしていた。
参考URL: Concatだけでビルドを済ませてた例(Backbone.jsとAngularJS) ::ハブろぐ https://havelog.ayumusato.com/develop/others/e613-concat_build_pattern_examples.html
ただし、ES6でもモジュール機構が標準で実装されているし、コンポーネント指向といった考えも出てきていて今度はモジュール単位に分割したものを再利用する流れが主流になってくると予想されるため、モジュール管理した開発手法に慣れておくのは重要だと思う。
CommonJSとAMD
モジュール定義と依存解決のAPI仕様(スタイル)としては大きく2つがある。それがCommonJSのModuleとAMD
JavaScriptは元々クライアント側の言語だが、普及するにつれてサーバーサイドもJavaScriptで書きたいという需要が増えてきた。
最初のうちは開発者が自分でAPIの仕様を決めて実装していたが、そのうち共通の仕様を決めようという流れが起きた。それがCommonJS。
当初はサーバーサイド(ServerJS)というAPI仕様だけを定めていたが、その内それ以外の仕様も含めようとなり、CommonJSに改名された。
モジュールの仕様に関してはCommonJSで策定されたわけではなく、Node.jsの実装が先行し、それがCommonJSに採用された形。
そのためCommonJSのモジュールを説明するときにNode.jsのコードが説明に持ち出されることは多い。例えば以下のようなコード。
# human.js
module.exports.getFullName = function() {
//
};
# action.js
var fullName = require('human').getFullName;
exports.sayHello = function() {
return 'Hello' + fullName;
};
# main.js
require('action').sayHello;
このCommonJSのモジュール仕様にそって書いたJavaScriptをブラウザ上で動かせるようにしたモジュールシステムがBrowserify。
Browserifyは実行時にrequireに指定されたモジュールを読み込むというアプローチではなく、事前にrequrie部分を書き換えるビルドプロセス。依存関係もこの時に解決する。
AMDとは
AMDはAsynchronous Module Definitionの略で、モジュールを非同期でロードする仕組みやAPI定義のことを指す。
AMDも元々、CommonJSで仕様を決めていたが、決まりきらず別グループに移動し策定された。
AMDの仕様を実装したのが、RequireJS。
RequireJSのモジュール定義と読み込みは以下のようなコード
# util.js
//モジュール定義
define(function() {
//定義するモジュールを持つオブジェクトを返す
return {
add: function(a, b) {
return a + b;
}
}
});
# main.js
// 読み込み
require(['util'], function(util) {
var result = util.add(10, 21);
console.log('10 + 21 = ' + result);
});
Browserifyとは異なり、ビルドで依存関係を解決するのではなく、実行時に依存関係を解決する。
RequireJSはモジュールシステムではかなり、先駆け的な存在だが、Node.jsの普及もあり、CommonJSスタイルで書けるように(Browserify or webpack)などで書く方が主流になりつつあるらしい。
webpackとは
webpackとはRequireJSやbrowserifyの後発のモジュールシステムでCommonJS、AMD両方の書き方があり、色々便利な書き方が出来るらしい。今後触ってみたい。
最後に
非同期ロードについて、非同期でJavaScriptを読み込むのでDOM構築を邪魔しない?くらいにしか理解できていないので、そこに関しては別記事にまとめたいと思う。