mithrilを利用してフロントエンド体感速度を改善する

自社サイトが遅いとの声が方々から上がって大変です！
そこでページ表示速度を改善して、自社サイトの表示速度を速くしました。
それでも、まだ遅いと言われました。。。

色々調査していると、「遅い」には２つの意味があることが判明しました。
「ページの表示速度」と「ページ内処理の動作速度」です。

「ページの表示速度」は改善傾向にあるので、「ページ内処理の動作速度」での改善が私のタスクです。
これを、体感速度改善と命名し、自社サイトの処理の一部を高速化しようと思います。
その方法の一つとして、VirtualDOMの利用は有効なのではという仮説の検証を実施します。

# 解決したい問題
- 動作速度の改善

# VirtualDOM比較
自分の主観が入りまくってます。。

| ツール | URL | 言語 | 環境 | ライセンス | 五年後どんな感じ？ | 所感 |
|:--|:--|:--|:--|:--|:--|:--|
| react.js | http://facebook.github.io/react/ | javascript, jsx | html５必須 | 必要なのはFRP（functional reactive programing） | 主流になっているのでは |このツールを利用したら、それ以外できなそうなので期待値は高いが不向きかも。一部機能でいじるだけなのに、どんだけコンポーネント読まなきゃいけないんだというかんじ。FLUX, react等こちらがやりたいことに比べて、できることが多すぎる |
| mithril.js | http://mithril-ja.js.org/getting-started.html <br/> http://www.tejitak.com/blog/?p=1577" | javascrip | ブラウザ標準記載なし ソース見る限りピュアなjs | MITライセンス | なくなっているかも<br/>しかし、コードがそれほど難しくないので自分でメンテ可能 | シンプルMVCフレームワーク<br/>reactのいいとこ取りしたフレームワーク<br/>海外で話題になり最近oraily本出版、codezine出稿<br/>パフォーマンステストでは現在最強" |
| Incremental DOM | https://medium.com/google-developers/introducing-incremental-dom-e98f79ce2c5f | javascript | リリースしたばかりで情報不足 | | | |
| elm | http://elm-lang.org/ | elm | haskwell | elmという言語を利用してjavascriptをコンパイルする | なくなっているかも | mvcフレームワークhaskwellベース<br/>早そうだが現状elmの学習コストが高い |
| mercury | https://github.com/Raynos/mercury | javascript | node利用<br/>ie9~ | MITライセンス | なくなっているかも| mithril同様速い、nodeを利用しているので、自社サーバの兼ね合いをどうするか |
| 自分で実装 | javascript | 自分のメンテしだい | jsonによる差分レンダリングを実装。mithrilのソースを見たらいけそうなきがした |

#処理速度比較
こちらで、計測させていただきました。
http://jsperf.com/angular-vs-knockout-vs-ember/355

#mithrilを利用してVirtualDOMを実装に決定
理由は3つです。

- 今後何かあっても自分でメンテできそう
- 自分たちが必要な機能だけあったこと
- jqueryだらけの自社サイトの一部でも利用できそうなこと

#プロト作成
とりあえず表示するやつ。

###コントローラーとビュー

```cart.js
var cartComponent = {
    controller: function () {
    vm.init();
    },
    view: function (controller) {
    	var self = this;
        return [m("[id='rightcartframe_messege']", {style: {"display": "none"}}),
	        m("[id='small_cart_souryou']", 
	        [m(".textXS", {style: {"padding": "5px", "margin-bottom": "0px"}}, 
	        ["冷凍手数料",m("font[color='#cc0000']", vm.cart().coolPrice()),m("br"),
	        m("span[id='reitou_waribikigaku']", ["（あと",m("font[color='#cc0000']", vm.cart().discountCoolPrice()),"で冷凍手数料無料に）"]),
	        m("br")]),m(".textXS", {style: {"padding": "5px"}}, ["送料",m("font[color='#cc0000']", vm.cart().postage()),m("br"),
	        m("span[id='souryou_waribikigaku']", ["（あと",m("font[color='#cc0000']", vm.cart().nextPostage()),"で送料",
	        m("font[color='#cc0000']", vm.cart().discountPostage()),"に"]),"）",m("br"),"お届け地域により",
	        m("font[color='#cc0000']", "追加送料"),"がかかります",m("hr")])]),
	        m("[id='small_cart_goukei']", "商品合計"+vm.cart().priceInNotTax()+"円（税抜）"),
	        m("[id='flame_cart_shouhin']", {style: {"height": " 304.2px"}}, [
	        m("p.flame_cart_shouhin__title", "野菜"),
	        vm.cart().vegitableItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "フルーツ"),
	        vm.cart().fruitItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "乳製品・たまご"),
	        vm.cart().milkAndEggItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "納豆・豆腐・漬物"),
	        vm.cart().soyItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "魚・水産加工品"),
	        vm.cart().fishItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "精肉"),
	        vm.cart().meetItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "ハム・ソーセージ"),
	        vm.cart().hamAndSausageItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "パン・米・麺類"),
	        vm.cart().panAndRiceItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "冷凍食品・そうざい"),
	        vm.cart().coolItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "調味料・乾物"),
	        vm.cart().seasoningItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "菓子"),
	        vm.cart().candyItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "飲料・酒"),
	        vm.cart().drinkItems().map(function(cart) {
	       return self.itemView(cart);}),
	        m("p.flame_cart_shouhin__title", "ホーム＆キッチン")]),
	        vm.cart().homeAndkitchenItems().map(function(cart) {
	       return self.itemView(cart);}),m("br[clear='all']")];
        },
    itemView: function (item) {
      return [m(".cart_item_box[id='shouhin_last']",
	         {style: {"margin-left": " 0px"}}, [m(".float_left", {style: {"padding": "5px"}}, 
	         [m("a[href='javascript:void(0)'][onclick='$.openShouhinShousai(\'ss3-8969\',\'1543\', \'http://www.oisix.com/ShouhinShousai.SHOUHINID_SHOUHINVER.htm\', \'A\');return false;']", 
	         [m("img[alt='"+item.name+"'][border='0'][src='"+item.imgPath+"'][width='50']")])]),m(".float_left", {style: {"width": "160px", "line-height": "1.5em"}}, 
	         [m("div", [m(".small_cart_icon_block.float_left", {style: {"width": "110px"}}, 
	         item.icons.map(function(icon) {
	         return [m("a[href='http://www.oisix.com/welcome?page=StaticBean&NEXTVIEW=g6/shopping/wappenlist.html'][onclick='SubWin(\'http://www.oisix.com/welcome?page=StaticBean&NEXTVIEW=g6/shopping/wappenlist.html\');return false;'][target='_blank']", 
	         [m("img[align='absmiddle'][alt=''][border='0'][height='14'][src='"+icon+"'][width='14']", {style: {"margin-right": "1px"}})]
	         )];})
	         ),m(".float_right", [m("a.shouhin_delete[data-id='CARTIDXDEL"+item.itemId+"_"+item.version+"_1'][href='javascript:void(0);']",{onclick:m.withAttr("data-id",vm.delete)} ,
	         [m(".shouhin_delete_img"),m("div", {style: {"display": "none"}}, "CARTIDXDEL"+item.itemId+"_"+item.version+"_1")])]),m(".float_right", [m(".okiicon[onclick='dispOkipop(\'ss3-8969\',\'1\', \'g6/shopping/CartShousaiView.jsp\', this, \'L\');'][onmouseout='hideMysetNavi();'][onmouseover='dispMysetNavi(this);']", {style: {"width": "28px", "height": "18px"}}, 
	         [m("img[alt='Myセット未登録'][id='OKIICONss3-8969'][src='g6/shared/image/icon_myoki03.gif']")])])]),
	         m("div", {style: {"clear": "both"}}, [m(".float_left.shouhin_name", {style: {"width": "120px", "line-height": "1.2em"}}, item.name),
	         m(".float_right", {style: {"line-height": "1.2em", "text-align": "right"}}, ["\518",m("br"),"×1"])])])])];
    }
};
```

###モデル

```cart.js
cartComponent.Cart = function(data){
 this.postage = m.prop(data.postage);
 this.discountPostage = m.prop(data.discountPostage);
 this.nextPostage = m.prop(data.nextPostage);
 this.coolPrice = m.prop(data.coolPrice);
 this.discountCoolPrice = m.prop(data.discountCoolPrice);
 this.priceInNotTax = m.prop(data.priceInNotTax);
 this.priceInTax = m.prop(data.priceInTax);
 this.vegitableItems = m.prop(data.vegitableItems);
 this.fruitItems = m.prop(data.fruitItems);
 this.milkAndEggItems = m.prop(data.milkAndEggItems);
 this.soyItems = m.prop(data.soyItems);
 this.fishItems = m.prop(data.fishItems);
 this.meetItems = m.prop(data.meetItems);
 this.hamAndSausageItems = m.prop(data.hamAndSausageItems);
 this.panAndRiceItems = m.prop(data.panAndRiceItems);
 this.coolItems = m.prop(data.coolItems);
 this.seasoningItems = m.prop(data.seasoningItems);
 this.candyItems = m.prop(data.candyItems);
 this.drinkItems = m.prop(data.drinkItems);
 this.homeAndkitchenItems = m.prop(data.homeAndkitchenItems);
  
 this.description = m.prop(data.orderId);
 this.done = m.prop(false);
 this.isLogin = m.prop(data.isLogin);
};
cartComponent.Cart.add = function(){
  return m.request({method: "GET", url: "/test.json", type: cartComponent.Cart});
};
cartComponent.Cart.list = function(){
  return m.request({method: "GET", url: "/test.json", type: cartComponent.Cart});
};
cartComponent.Cart.delete = function(){console.log(vm.description());
  return m.request({method: "GET", url: "/remove.json", type: cartComponent.Cart});
};
```

##ビューモデル

```cart.js
var vm = {
  init:function(){
  	vm.description = m.prop("");
    vm.cart = cartComponent.Cart.list();
    vm.add = function(){
    	vm.cart = cartComponent.Cart.add();
    };
    vm.delete = function(id){
      vm.description(id);
      vm.cart = cartComponent.Cart.delete();
    };
  }
};
```

**今後、実装した気づきを追記していきます！**