SPA開発（OnsenUI）でちょっとだけ可読性を意識する

# はじめに
SPA（Single Page Application）なアプリを作りたいと思ったら、JavaScriptはどうしても複雑になってきます。
なのでAngularだったりReactだったりを使い始めて、エンジニアたるものその辺りも学習したりと、色々頑張るわけです
とはいえ、実際の現場ではjQueryでDOMいぢる程度な人が
「私JavaScriptマスターしてますけど何か？」
ってノリでフロントエンジニアとしてアサインされたりして、でも納期はカツカツで教えてる暇も無ければ、別な人もアサインできない、、、
みたいな状況は往々にしてあるわけで
ということで、そんなケースも想像して、こんな感じでSPA開発をしていますというお話
ちなみにサンプルコードはmonacaでOnsenUIを使用しています

# 1.1画面ごとに jsとcssをそれぞれ1つずつ用意する
特筆すべきことでも無い気もしますが
ディレクトリ構成はこんな感じになります
![img1.png](https://qiita-image-store.s3.amazonaws.com/0/100337/2eda1c73-7e5b-f4bb-41c7-8c92837851d6.png)


Top,hogeList,hogeDetailの3画面構成です
css/page配下 と js/page配下がそれになります
画面と対なファイル構成なので、修正箇所はわかりやすいっちゃーわかりやすいです

ちなみに js/lib はネットで拾ってきたライブラリ
js/mylibは共通クラスだとか自分で作ったライブラリ置き場みたいな感じです

# 2.HTMLのID属性は基本 画面に対してのみ
```html:top.html
<ons-page id="onsPageTop">
    <div id="pageTop" class="pageContainer">
    
        <!--
        コンテンツ内容色々
        -->    
    
        <div class="goToListRow">
            <div class="nextButton">リストへ行く</div>
        </div>
    </div>
</ons-page>
```
HTMLはこんな感じになっていて、id属性は 画面をくくったdivに対して指定するのみで
その他HTMLの要素にはid属性をつけずに class属性のみで頑張るというルールにしています

# 3.jQueryやCSSのセレクタ指定は画面IDの孫指定をする
2に付随してHTMLとCSSはこんな感じにしていて

```css:page/top.css
#pageTop .goToListRow{
    padding:20px 40px;
    text-align: center;
}
#pageTop .nextButton{
    border:1px solid #000;
    padding:20px;
    background-color: #ccc;
}
```
```js:page/top.js
var TopController = (function(){
    function self(){}
    
    self.init = function(){
        $('#pageTop .nextButton').click(function(){
            appNavigator.pushPage('hogeList.html');
        });
    };
    return self;
})();
$(document).on('pageinit' , '#onsPageTop' , TopController.init);
```

特にJSが面倒なんですが、必ず画面divのIDの子孫指定としています
他の画面のDOMへのセレクタが重複して、想定外のところ書き換えちゃうのが怖くてこうしています

なお、OnsenUIに限った表現になりますが、こうやってもpopPageやresetToPageなどせずにpushPageを繰り返し、スタックに同一ページが複数ある場合はおかしくなってしまいます
例えば友達一覧画面と友達プロフィール画面があった時に
friendList.html(自分の友だち一覧)
　→friendProfile.html(自分の友だちAさんのプロフィール)
　　→friendList.html(Aさんの友だち一覧)
　　　→friendProfile.html(Aさんの友だちBさんのプロフィール)
こんな感じでpushPageが3回発生する画面遷移があるとすると、2個目のfriendProfile.htmlだけをBさん表示にしたかったのに、1個目のfriendProfile.htmlも変わっちゃっていて
popPageを2回おこなうとAさんではなくBさんの情報が表示されちゃうみたいな
そういう時はこんな感じで対応できます

```js:page/top.js
    self.init = function(){
        var pageElement = appNavigator.getCurrentPage().element;
        pageElement.find('.nextButton').click(function(){
            appNavigator.pushPage('hogeList.html');
        });
    };
```

# 4.動的に行を描画する時、ちょっとでも複雑だったらテンプレートっぽいのを用意する
HTMLとJSをこうしてます

```html:hogeList.html
<ons-page id="onsPageHogeList">
    <div id="pageHogeList" class="pageContainer">
        <div class="myList"></div>
        <!--常に非表示の行テンプレート Start-->
        <div class="myListRowTemplate" style="display:none;">
            <div class="myListRow">
                <div class="leftCol">
                    <div class="item1"></div>
                </div>
                <div class="rightCol">
                    <div class="item2"></div>
                    <div class="item3"></div>
                </div>
            </div>
        </div>
        <!--常に非表示の行テンプレート End-->
        <hr />
        <div class="buttonRow"><ons-button onclick="App.backPage();">戻る</ons-button></div>
    </div>
</ons-page>
```

```js:page/hogeList.js
var HogeListController = (function(){
    function self(){}
    
    self.init = function(){
        var listElm = $('#pageHogeList .myList');
        listElm.empty();
        var dataList = DummyData.getSampleList();
        for(var i=0; i<dataList.length; i++){
            listElm.append( self._createRowElement(dataList[i]) );
        }
    };
    self._createRowElement = function(dataRow){
        var rowElm = $($('#pageHogeList .myListRowTemplate').html());
        rowElm.find('.item1').text(dataRow.item1);
        rowElm.find('.item2').text(dataRow.item2);
        rowElm.find('.item3').text(dataRow.item3);
        return rowElm;
    }
    return self;
})();

$(document).on('pageinit' , '#onsPageHogeList' , HogeListController.init);
```

JSで生成したDOM確認なんて、デバッガーで余裕なものの
やっぱ、JS上でcreateElementしたり、htmlの文字列をappendしたりとかだと
仕様変更時などにコードを確認する時間が長くなってしまったり、
やや例外チックな対応をしたいのに小回りがきく感じでパパッと修正できなかったりすることがあったので、こうやっています

# 最後に
上記に加えて、TypeScriptで書いちゃおうかなって気分なプロジェクトの場合は
連想配列チックなことはなるだけやらずに
いわゆるValue Object的なClassを書いていきましょーみたいなルールを設けています
（可読性ではなくバグ防止みたいな観点で）

こんな感じでアプリ作ってるんですが
・描画などのパフォーマンスを重視する
・開発メンバーにデキる人がそれなりにいて、スマートに書きたい
みたいな場合は、今回の内容は大体無視した方がよいかと思います