SI部のオールドルーキー内田です。
今回は、「JavaScript大規模開発はじめの一歩」と題しまして、Webアプリケーション開発におけるクライアントサイド、
HTML+JavaScriptの記述方法についてのTipsをお届けしたいと思います。
対象読者は「ちょっとJavaScriptが苦手なWebアプリケーション開発者」を想定していますよ。
さっそく始めます
とりあえず下記のようなHello World的なHTMLファイルがあるとします。
casley0.html
<!doctype html> <html> <head> <meta charset="utf-8"> <script type="text/javascript"> function sayHello(msg){ window.alert(msg); } </script> </head> <body> <div> <input id="button" type="button" value="押してね" onClick="sayHello('こんにちは、キャスレー')"></input> </div> </body> </html>
「ある程度の規模でのチーム開発を想定したWebアプリケーションを構築する」という観点に立つと、「表示と実装を分離したい」という要望が出てくると思います。 先の例に当てはめると「HTMLファイルから処理記述を排除したい」ということで、まずHTMLファイルに処理が混入しているのが良くないように見えます。
というわけで…
HTMLの記述とJavaScriptの記述を分割しよう
JavaScriptの処理を外部ファイル化してみましょう。
casley1.html
<!doctype html> <html> <head> <meta charset="utf-8"> <script type="text/javascript" src="js/casley1.js"> </head> <body> <div> <input id="button" type="button" value="押してね" onClick="sayHello('こんにちは、キャスレー')"></input> </div> </body> </html>
js/casley1.js
function sayHello(msg){ window.alert(msg); }
また、ボタンのonClick属性に関数実行処理を記述したくないので、スクリプト内でボタン要素にイベントを関連付けしましょう。
casley2.html
<!doctype html> <html> <head> <meta charset="utf-8" /> <script type="text/javascript" src="js/casley2.js"></script> </head> <body> <div> <input id="button" type="button" value="押してね"></input> </div> </body> </html>
js/casley2.js
function init(){ document.getElementById("button").addEventListener('click', function(){ window.alert('こんにちは、キャスレー'); }); } window.onload = init;
ここで、このサンプルでは「init()」という名前の関数を定義していますが、この関数はグローバル領域に宣言されており、
インクルードしたすべてのscript内で参照可能な状態になっています。
従って、意図しない名前被りが発生する可能性が高く、スクリプトエラーが発生したり、変な値が代入されてしまったりなど、不正な結果発生に繋がりやすい危険な行為になってしまうのですね。
このようなグローバル領域への変数宣言は「グローバル汚染」と呼ばれ、特に大規模なJavaScript開発を行う場合、可能な限り避けるべきとされています。
即時関数式で処理記述してグローバル汚染を回避しよう
グローバル汚染を回避する形にスクリプトを書き直します。
casley03.html
<!doctype html> <html> <head> <meta charset="utf-8" /> <script type="text/javascript" src="js/casley3.js"></script> </head> <body> <div> <input id="button" type="button" value="押してね"></input> </div> </body> </html>
js/casley03.js
(function(){ function init(){ document.getElementById("button").addEventListener('click', function(){ window.alert('こんにちは、キャスレー'); }); } window.onload = init; })();
関数を宣言した直後に実行しています。「即時関数式」と呼ばれる記法です。
慣れないとちょっと読みにくいかもしれません。
「見たことあるけど深く意味考えないでスルーしてきた・・」みたいな方もいるんじゃないかと思います。
書式上は、下記のような記法と等価になりますね。
(window.alert)('こんにちは、キャスレー');
jQuery, BootStrap
とりあえずJQueryとBootStrapも入れましょう。
casley4.html
<!doctype html> <html> <head> <meta charset="utf-8"> <script src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.js' type='text/javascript'></script> <link rel="stylesheet" href="http://getbootstrap.com/dist/css/bootstrap.css" /> <link rel="stylesheet" href="css/casley.css" /> <script src='http://getbootstrap.com/dist/js/bootstrap.js' type='text/javascript'></script> <script type="text/javascript" src="js/casley4.js"></script> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"></nav> <div class="container"> <div class="jumbotron"> <h1>ようこそ</h1> <p>これはキャスレー技術ブログのサンプルサイトです。そんな感じです。</p> </div> <button id="button" type="button" class="btn btn-primary" >押してね</button> </div> </body> </html>
css/ccasley.css
body { padding-top: 70px; padding-bottom: 30px; }
js/casley4.js
(function(){ function init(){ $("#button").on('click', function(){ window.alert('こんにちは、キャスレー'); }); } $(window).load(init); })();
見た目はbootstrapのテーマサンプル(http://getbootstrap.com/examples/theme/)からちょろっと拝借しました。
少しいい感じになりましたね。
蛇足ですが、jquery・bootstrapはデプロイの際はxxxx/jquery.min.js / xxxxx/bootstrap.min.css / xxxxx/bootstrap.min.jsとしたファイルを使用するのが一般的です。「xxx.min.xx」ファイルはminify(ミニファイ)ファイルと呼ばれており(「xxx.min.xx」を生成することをminifyする、などと言います)、元ファイルから改行・スペースを除去した形に不要なバイトを取り除く事によりファイル転送・解析・実行速度が向上する効果がありますが(リバースエンジニアリングしにくくなる意味で、簡易的なセキュリティ向上効果もあるといえばあります。)
開発時はデバッグしづらい点が無視できないデメリットになるので、ミニファイされていないファイルの使用をお勧めしたいと思います。
共通関数とアプリケーション処理を分割する
今回の仕上げです。
サンプル中の警告表示処理 alert()は、とりあえず標準のダイアログを表示していますが、あとでプロジェクト独自の部品への差し替えるため
プロジェクトの共通関数を呼び出す形に書き換えておきましょう。
casley5.html
<!doctype html> <html> <head> <meta charset="utf-8"> <script src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.js' type='text/javascript'></script> <link rel="stylesheet" href="http://getbootstrap.com/dist/css/bootstrap.css" /> <link rel="stylesheet" href="css/casley.css" /> <script src='http://getbootstrap.com/dist/js/bootstrap.js' type='text/javascript'></script> <script type="text/javascript" src="js/casley_common.js"></script> <script type="text/javascript" src="js/casley_logic.js"></script> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"></nav> <div class="container"> <div class="jumbotron"> <h1>ようこそ</h1> <p>これはキャスレー技術ブログのサンプルサイトです。そんな感じです。</p> </div> <button id="button" type="button" class="btn btn-primary" >押してね</button> </div> </body> </html>
css/casley.css
body { padding-top: 70px; padding-bottom: 30px; }
js/casley_common.js
(function(){ window.casley = {}; var casley = window.casley; casley.alert = function(msg){ window.alert(msg); } })();
js/casley_logic.js
(function(){ function init(){ $("button").on('click', function(){ window.casley.alert('こんにちは、キャスレー'); }); } $(window).load(init); })();
JavaScriptファイルを共通関数定義ファイル casley_common.jsとアプリケーション処理定義ファイル casley_logic.jsに分割しました。
アプリケーション処理定義ファイルは画面毎に別ファイルとする、のような構成を想定する形とします。
共通関数・アプリケーション処理いずれも即時関数式で記述してあり、グローバル汚染を回避しています。
共通関数の定義方法
ここで、共通関数・変数・定数は性質上、定義がグローバル領域から参照可能である必要があり、ある意味グローバル汚染が避けられない状況になるのですが、
デメリットを最小限にとどめる方法として、共通関数群を(自分たちのプロジェクト名など、名前被りの生じにくい名称で)パッケージ化して宣言する方法があります。
今回は、共通関数の宣言は即時関数内で行ない、処理実体はwindowオブジェクト配下に配置して外部から参照できる構成としました。
他にも、例えばjQueryの拡張ライブラリーとして定義したり、グローバル領域にパッケージを宣言したりする方法もあると思います。
まとめ
幾つかポイントがあったと思いますが、
- HTMLとJavaScriptの処理を分割する
- 外部ファイルでは即時関数式を使用しグローバル汚染を回避する
- 共通関数・共通変数はプロジェクト名でパッケージ化するなどグローバル領域で識別可能にしておく
このような観点で処理を整理することで、複数のメンバーによるコード共有に耐えられる、ある程度大規模なWebアプリケーション開発を行なう準備ができたと思います。
今回ご紹介した方法がクライアントサイドの開発効率を上げる一助となれば幸いです。
最後までお読みくださり、ありがとうございました。