Angular, knockoutjs, React, Vue.js の使用感とメモリ使用量と速度の記録（2020年７月）

# いわゆるUIライブラリ（[Angular], [knockoutjs], [React], [Vue.js]）の使用感とメモリ使用量と速度の記録（2020年７月）

プログラマのみなさまこんにちは😉。[ハーツテクノロジー]の山崎です。この記事は仕事の中で得たられた知見によって書かれています。

[ハーツテクノロジー]:https://www.hearts-tech.co.jp/

で、いきなり冒頭から脱線してお詫びから入りますが、「UIライブラリ」という言葉はそのものズバリではないですよね。すみません。
「UIライブラリ」と書くと [Bootstrap] のようなCSS系ライブラリがメインのような気がしますし、「JSライブラリ」と書くと、広すぎて、UIとは無関係のライブラリが含まれてしまいます。「フレームワーク」も同様に広すぎるし、なんというか、データバインド系のJSライブラリの総称があるとよいのですが、いまのところ見つけられないので「UIライブラリ」を使わせていただいています。ピッタリな言葉がありましたらコメントください。よろしくお願いいたします。🙇‍♂️

[Bootstrap]:https://getbootstrap.com/

# 0. 背景「UIライブラリをどういう基準で選んだらよいのだろうか？🤔」

フロントエンドUIライブラリってたくさんあって、どれを、どういう基準で選んだらよいのだろうか？ いろいろな意見があるけど「実際の数値はどうなんだろう？」と気になった。うわさや感覚だけではなく、実際の数値を見て判断材料にしたいと思った。

### 今回調べたUIライブラリとそのバージョン

- [Angular] 1.8.0
- [knockoutjs] 3.5.1
- [React] 16.13.1
- [Vue.js] 2.6.11

[Angular]:https://angularjs.org/

[knockoutjs]:https://knockoutjs.com/

[React]:https://reactjs.org/docs/add-react-to-a-website.html

[Vue.js]:https://jp.vuejs.org/v2/guide/

方針としては、同じ課題を４種類のUIライブラリで実装してみて、以下のポイントを比べてみることとした。

・コード量、書きやすさ、可読性。
・Chrome で起動したときの使用メモリ(KB)と速度(fps) 。

Chrome 以外のブラウザでの調査は保留。コードはすべてまるっと CodePen に置いたので、気になるひとは各自の環境で実行してみてくださいませ。

で、なぜこの４つを選んだのか？というと、特に理由はなくて、なんとなく有名どころを順に選んでみました。

[Riot.js] が入っていないのは「その昔、[Riot.js] で SVG を表示しようとしてひどい目にあった記憶が蘇るから」という個人的な理由だったりします。いまは改善されているのでしょうか？（詳しい方のフォローのコメントをお待ちします😅）

[Riot.js]:https://riot.js.org/ja/

# 1. ミッション→各UIライブラリで table と svg をたくさん表示してみる🎉

なるべく、コンパクトでわかりやすく、処理性能がわかるような課題を選んだ。
先に、動いているところを見てもらったほうが理解が早いと思うので、アニgifを貼ります。こんな感じ👇
![2020-07-22.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/cba29621-a8e4-6e29-93d8-841f1a09510b.gif)

ざっと説明すると、「矩形領域の中を跳ねまわる円を SVG で描いて、その座標を table に書く。それを setInterval() を使って 60fps でリアルタイムに動かす！おまけに object の数もリルタイムに変えられるようにする」というもの。なので、以下のコードがおおむね共通になります。

```JS
const AREA_W = 333
const AREA_H = 111

class Point {
  constructor( _no ) {
    this.no = _no
    this.color_h = _no * 53 % 360
    this.x = Math.random() * AREA_W
    this.y = Math.random() * AREA_H
    this.movespeed_x = (Math.random() - 0.5)
    this.movespeed_y = (Math.random() - 0.5)
  }
  move() {
    this.x += this.movespeed_x
    this.y += this.movespeed_y
    if ( this.x < 0      ) { this.x = 0; this.movespeed_x *= -1 }
    if ( this.y < 0      ) { this.y = 0; this.movespeed_y *= -1 }
    if ( this.x > AREA_W ) { this.x = AREA_W; this.movespeed_x *= -1 }
    if ( this.y > AREA_H ) { this.y = AREA_H; this.movespeed_y *= -1 }
  }
}

const vm = {
    p_list:   [],
    p_size:   0,
    max_size: 200,
}

setInterval( ()=>
{
    if ( vm.p_size != vm.max_size ) { // 再構築
        vm.p_list = []
        for ( let i=0 ; i<vm.max_size ; ++i )
            vm.p_list.push( new Point( i+1 ) )
        vm.p_size = vm.max_size
    }

    for ( let p of vm.p_list ) p.move()  // 移動

}, 1000/60 ) // 60fps
```

Qiita を読んでいるひとには解説は不要なくらいシンプルに書いたつもり。

 1. class Point に座標(x,y), 色(color_h), 移動速度(movespeed_x, movespeed_y)を持って。move() メソッドで座標を移動し、範囲(AREA_W, AREA_H)からはみ出さないように位置と速度を調整

 2. max_size が object 数の入力値で、 p_list に Point object を入れる

 3. 最後に、setInterval() を使って、60fps で p_list 内のすべての object を移動

常に変化するデータ p_list を、どのくらいリアルタイムに表示できるか？　また、どのくらいのコード量で記述＆表現できるのか？を調べるのがこのエントリのミッションとなります。

この共通コードはおおむね 40行なので、表示のために増えたコード量が UIライブラリ特有の記述量と考えてよいはず。

（念の為に、以下に実行環境の情報を貼って置きます。基本、スルーで。）

### 使用した Chrome のバージョン

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/bf2119cc-6c0d-4b68-4d8b-1c4059cbc900.png)

### 使用した PC のスペック 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/0bdc0c80-b93c-b4a9-a866-1a2ee52aa756.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/7487baf5-06b1-cc75-abf2-d55463d007c2.png)

ちなみに、ディスプレイのリフレッシュレートは 60Hz です。計測した日は 2020-07-21 です。

# 2. コード量と書きやすさ

ここでのポイントは、なるべく１行の意味が同じになるように（UIライブラリごとの不公平感が少なくなるように）注意しながら書いたところ。
また、読みやすさ、理解しやすさにも注意をはらいました。

ブラウザを２つ左右に並べてこのエントリを表示＆比較することで、それぞれのUIライブラリの書き方の特徴（どのあたりが同じで、どのあたりが異なるのか）がわかると思います。

先に、全体のコードを書いた感想を書かせていただくと、４つのUIライブラリで書き方に大きな違いは感じませんでした。ですので、書き方で比較するとなるとどうしても重箱の隅を突く傾向が避けられない感じです。

## [Angular] 1.8.0 のコード(73行)

コードを書いてみた感想は、`$scope`, `$interval` という引数 object に追記していく書き方が特徴的でした。
あと、グローバルな変数は参照できないようで `$scope.AREA_W = AREA_W` のように、ローカルな位置に転送する必要が見られた。

「[Angular]は、svg が苦手」という話をどこかで聞いたような気もするけど、特に、気にせず書きたいように書いて動いてしまいました。（昔の話だったのかな？）

全体的に難しいこともなく、悩むこともなく、以外に普通だった。

```HTML
<a href="https://angularjs.org/" >AngularJS 1.8.0</a> SVG example

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.0/angular.min.js"></script>
<div ng-app="app" ng-controller="AngularSVG">
    <div style="width: 333px; height: 111px; overflow: auto;" >
        <table style="width: 300px; border: solid 1px;" >
            <thead>
                <tr><th>No</th><th>Color</th><th>X</th><th>Y</th></tr>
            </thead>
            <tbody>
                <tr ng-repeat="p in p_list">
                    <td style="text-align: center;">{{ p.no }}</td>
                    <td style="text-align: right;">{{ p.color_h }}</td>
                    <td style="text-align: right;">{{ p.x.toFixed(2) }}</td>
                    <td style="text-align: right;">{{ p.y.toFixed(2) }}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <hr/>
    <svg ng-attr-width="{{AREA_W + 5 * 2}}" ng-attr-height="{{AREA_H + 5 * 2}}" stroke="#111" fill="#ddd" >
        <rect x="0" y="0" ng-attr-width="{{AREA_W + 5 * 2}}" ng-attr-height="{{AREA_H + 5 * 2}}" ></rect>
        <circle ng-repeat="p in p_list" ng-attr-cx="{{p.x + 5}}" ng-attr-cy="{{p.y + 5}}" r="5px" ng-attr-fill="{{'hsl('+ p.color_h +', 75%, 75%)'}}" ></circle>
    </svg>
    <div> max_size: <input type="number" ng-model="max_size" /> {{ max_size }}</div>
</div>
<script>

const AREA_W = 333
const AREA_H = 111

class Point {
  constructor( _no ) {
    this.no = _no
    this.color_h = _no * 53 % 360
    this.x = Math.random() * AREA_W
    this.y = Math.random() * AREA_H
    this.movespeed_x = (Math.random() - 0.5)
    this.movespeed_y = (Math.random() - 0.5)
  }
  move() {
    this.x += this.movespeed_x
    this.y += this.movespeed_y
    if ( this.x < 0      ) { this.x = 0; this.movespeed_x *= -1 }
    if ( this.y < 0      ) { this.y = 0; this.movespeed_y *= -1 }
    if ( this.x > AREA_W ) { this.x = AREA_W; this.movespeed_x *= -1 }
    if ( this.y > AREA_H ) { this.y = AREA_H; this.movespeed_y *= -1 }
  }
}

angular.module('app', [])
.controller('AngularSVG', function($scope, $interval) {

    $interval( ()=>
    {
        if ( $scope.p_size != $scope.max_size ) { // 再構築
          $scope.p_list = []
          for ( let i=0 ; i<$scope.max_size ; ++i )
              $scope.p_list.push( new Point( i+1 ) )
          $scope.p_size = $scope.max_size
        }

        for ( let p of $scope.p_list ) p.move()  // 移動
    }, 1000/60 ) // 60fps

    $scope.p_list   = []
    $scope.p_size   = 0
    $scope.max_size = 200

    $scope.AREA_W = AREA_W
    $scope.AREA_H = AREA_H
})
</script>
```

<p class="codepen" data-height="412" data-theme-id="dark" data-default-tab="html,result" data-user="yamazaki3104" data-slug-hash="RwreWXM" style="height: 412px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="AngularJS 1.8.0 SVG example"></p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## [knockoutjs] 3.5.1 のコード(82行)

コードのコメントにも書いたけど、配列 p_list の更新を [knockoutjs] に知らせる方法がわからなかった。setInterval() で vm.p_list は更新されているのだけど表示が変わらないという。仕方が無いので、removeAll() でいったん削除して、vm.p_list を作り直しています。速度が出ないのはこの書き方も理由だと思っています。（詳しいひとがおられましたらコメントいただけるとたすかります。🙏→ jun1s さんにコメントをいただきました！ありがとうございます。コードに関してはコメント欄をごらんください。次回の計測時にはきっと、おそらく[knockoutjs]もいい勝負をしてくれると思います。）

書き方が、`data-bind="hoge: fuga"` という記述で統一されているので、そこさえ理解してしまえば、難しいところは無い感じ、かな。

```HTML
<a href="https://knockoutjs.com/" >Knockout.js 3.5.1</a> SVG example

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.min.js"></script>
<div style="width: 333px; height: 111px; overflow: auto;" >
    <table style="width: 300px; border: solid 1px;" >
        <thead>
            <tr><th>No</th><th>Color</th><th>X</th><th>Y</th></tr>
        </thead>
        <tbody data-bind="foreach: p_list">
            <tr>
                <td data-bind="text: no" style="text-align: right;"></td>
                <td data-bind="text: color_h" style="text-align: right;"></td>
                <td data-bind="text: x.toFixed(2)" style="text-align: right;"></td>
                <td data-bind="text: y.toFixed(2)" style="text-align: right;"></td>
            </tr>
        </tbody>
    </table>
</div>
<hr/>
<svg data-bind="attr: { width: AREA_W + 5 * 2, height: AREA_H + 5 * 2 }" stroke="#111" fill="#ddd" >
    <rect x="0" y="0" data-bind="attr: { width: AREA_W + 5 * 2, height: AREA_H + 5 * 2 }" ></rect>
    <g data-bind="foreach: p_list" >
        <circle data-bind="attr: { cx: x + 5, cy: y + 5, fill: 'hsl('+color_h+', 75%, 75%)' }" r="5px" ></circle>
    </g>
</svg>
<div> max_size: <input type="number" data-bind="value: max_size" /><span data-bind="text: max_size"></span</div>

<script>

const AREA_W = 333
const AREA_H = 111

class Point {
  constructor( _no ) {
    this.no = _no
    this.color_h = _no * 53 % 360
    this.x = Math.random() * AREA_W
    this.y = Math.random() * AREA_H
    this.movespeed_x = (Math.random() - 0.5)
    this.movespeed_y = (Math.random() - 0.5)
  }
  move() {
    this.x += this.movespeed_x
    this.y += this.movespeed_y
    if ( this.x < 0      ) { this.x = 0; this.movespeed_x *= -1 }
    if ( this.y < 0      ) { this.y = 0; this.movespeed_y *= -1 }
    if ( this.x > AREA_W ) { this.x = AREA_W; this.movespeed_x *= -1 }
    if ( this.y > AREA_H ) { this.y = AREA_H; this.movespeed_y *= -1 }
  }
}

const vm = {
    p_list:   ko.observableArray([]),
    p_size:   0,
    max_size: ko.observable( 200 ),
}

setInterval( ()=>{

    if ( vm.p_size != vm.max_size() ) { // 再構築
        vm.p_list.removeAll()
        for ( let i=0 ; i<vm.max_size() ; ++i )
            vm.p_list().push( new Point( i+1 ) )
        vm.p_size = vm.max_size()
    }

    // 移動
    let nw = []
    for ( let p of vm.p_list() )
    {
        p.move()
        nw.push( p )
    }

    vm.p_list.removeAll()   // KO.js はいったん空にしないと、Object の中が変わっていても再描画対象にはしない模様。なので、全部入れ替えているのだけど、、、もっとよい方法がありましたら教えて下さい。
    for ( let p of nw )
        vm.p_list.push( p )

}, 1000/60 ) // 60fps

ko.applyBindings( vm )
</script>
```

<p class="codepen" data-height="436" data-theme-id="dark" data-default-tab="html,result" data-user="yamazaki3104" data-slug-hash="LYGMmrW" style="height: 436px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Knockout.js 3.5.1 SVG example"></p>


## [React] 16.13.1 のコード(97行)

JSX を通すのに、babel.min.js を使いました。JSX を使わないと、コードが長くなって、他のライブラリとの比較が困難になるからです。

さて、現在、一番人気と噂される [React] ですが、４つの中で、一番コードが長くなったのがこの [React] です。

[React] は class を継承するのがポイントでしょうか。他の書き方もあるかもしれませんが、短時間でそこまで調べることはできませんでした。必ず、state を更新するのもお約束なのでしょうか？こちらも調べている時間がありませんでした。いろいろ作法があるようで、短期間にちゃちゃっと学習して動かすのはちょっとハードル高めに感じました。

４つのUIライブラリ中で、記述に一番苦労した（悩んだ）のが [React] でした。みなさん、本当に [React] が使いやすい（書きやすい）と思って選んでいるのでしょうか？　ちょっと疑問に思えてきました。

```HTML
<a href="https://reactjs.org/docs/add-react-to-a-website.html" >React 16.13.1</a> SVG example 

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js" ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js" ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js" ></script><!-- https://cdnjs.com/libraries/babel-standalone -->
<div id='ReactSVG-example'></div>
<script type="text/babel" >

const AREA_W = 333
const AREA_H = 111

class Point {
  constructor( _no ) {
    this.no = _no
    this.color_h = _no * 53 % 360
    this.x = Math.random() * AREA_W
    this.y = Math.random() * AREA_H
    this.movespeed_x = (Math.random() - 0.5)
    this.movespeed_y = (Math.random() - 0.5)
  }
  move() {
    this.x += this.movespeed_x
    this.y += this.movespeed_y
    if ( this.x < 0      ) { this.x = 0; this.movespeed_x *= -1 }
    if ( this.y < 0      ) { this.y = 0; this.movespeed_y *= -1 }
    if ( this.x > AREA_W ) { this.x = AREA_W; this.movespeed_x *= -1 }
    if ( this.y > AREA_H ) { this.y = AREA_H; this.movespeed_y *= -1 }
  }
}

class ReactSVG extends React.Component {
  constructor(props) {
      super(props)

      this.state = {
          p_list:   [],
          p_size:   0,
          max_size: 200,
      }

      this.p_list_renewal = this.p_list_renewal.bind(this)   // つい忘れがち
  }

  p_list_renewal( ev ) {
      const mx = parseInt(ev.target.value)
      this.setState( st => { st.max_size = mx ; return st } )
  }

  componentDidMount() {
    setInterval(() =>
    {
        if ( this.state.p_size != this.state.max_size ) { // 再構築
          this.state.p_list = []
          for ( let i=0 ; i<this.state.max_size ; ++i )
              this.state.p_list.push( new Point( i+1 ) )
          this.state.p_size = this.state.max_size
        }

        for ( let p of this.state.p_list ) p.move() // 移動

        this.setState( st => st ) // KO.js と同じように、Object の中の値が変化しても再描画対象にはならない模様。
    }, 1000/60 )
  }

  render() {
    return (
      <div>
      <div style={{ width: "333px", height: "111px", overflow: "auto" }} >
        <table style={{ width: "300px", border: "solid 1px" }} >
          <thead>
            <tr><th>No</th><th>Color</th><th>X</th><th>Y</th></tr>
          </thead>
          <tbody>
            { this.state.p_list.map( (p,idx) => ( <tr key={ idx } >
              <td style={{textAlign: "center"}}>{ p.no }</td>
              <td style={{textAlign: "right"}}>{ p.color_h }</td>
              <td style={{textAlign: "right"}}>{ p.x.toFixed(2) }</td>
              <td style={{textAlign: "right"}}>{ p.y.toFixed(2) }</td>
            </tr> ) ) }
          </tbody>
        </table>
      </div>
      <hr/>
      <svg width={ AREA_W + 5 * 2 } height={ AREA_H + 5 * 2 } stroke="#111" fill="#ddd" >
          <rect x="0" y="0" width={ AREA_W + 5 * 2 } height={ AREA_H + 5 * 2 } ></rect>
          { this.state.p_list.map( (p,idx) => (
            <circle key={ idx } cx={ p.x + 5 } cy={ p.y + 5 } r="5px" fill={ 'hsl('+ p.color_h +', 75%, 75%)' } ></circle>
          ) ) }
      </svg>
      <div> max_size: <input type="number" value={ this.state.max_size } onChange={ this.p_list_renewal }/> { this.state.max_size }</div>
      </div>
    )
  }
}

ReactDOM.render( <ReactSVG />, document.querySelector( 'div#ReactSVG-example' ) )
</script>
```

<p class="codepen" data-height="424" data-theme-id="dark" data-default-tab="html,result" data-user="yamazaki3104" data-slug-hash="jOWeWxb" style="height: 424px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="React 16.13.1 SVG example"></p>

## [Vue.js] 2.6.11 のコード(75行)

４つのなかでは、比較的書きやすく、また読みやすくも感じました。ストレスが少ないです。一番感じたのは、データのバインディングのところで、他の３つは、少なからずなんらかの意識が必要で、例えば「このタイミングでデータを更新するから、表示も更新しなきゃ。そのためには、こう書かないと」という意識が頭の中から離れない感じがありました。しかし、[Vue.js]には、それはなく。表示とデータの更新を、特にタイミングなどを意識すること無く書いて、そのまま実行するだけで動いてしまうという感覚になりました。それはおそらく、[Vue.js] がデータバインディングに注意を払って作られているということなのでしょう。

~~他に、気になった点は、`template:` を使ったので、HTML tag を文字列として指定したためシンタックスハイライト機能が働かないので、目視チェックでガンバる必要があったこと。`<template>` タグを使えば回避できるかも？とちょっと思ったのだけど、安易に動かす方を優先してしまいました。~~ その後、`<template>` タグを使った書き方に更新しました。行数変わらず。(2020-08-07)。

```HTML
<a href="https://jp.vuejs.org/v2/guide/">Vue.js 2.6.11</a> SVG example 

<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script><!-- 2020-05-08 https://cdnjs.com/libraries/vue -->
<template id='tags'>
<div>
    <div style="width: 333px; height: 111px; overflow: auto;" >
        <table style="width: 300px; border: solid 1px;" >
            <thead>
                <tr><th>No</th><th>Color</th><th>X</th><th>Y</th></tr>
            </thead>
            <tbody>
                <tr v-for="p in p_list" >
                    <td style="text-align: center;">{{ p.no }}</td>
                    <td style="text-align: right;">{{ p.color_h }}</td>
                    <td style="text-align: right;">{{ p.x.toFixed(2) }}</td>
                    <td style="text-align: right;">{{ p.y.toFixed(2) }}</td>
                </tr>
            </tbody>
        </table>
    </div>
    <hr/>
    <svg v-bind:width="AREA_W + 5 * 2" v-bind:height="AREA_H + 5 * 2" stroke="#111" fill="#ddd" >
        <rect x="0" y="0" v-bind:width="AREA_W + 5 * 2" v-bind:height="AREA_H + 5 * 2" ></rect>
        <circle v-for="p in p_list" v-bind:cx="p.x + 5" v-bind:cy="p.y + 5" r="5px" v-bind:fill="'hsl('+ p.color_h +', 75%, 75%)'" ></circle>
    </svg>
    <div> max_size: <input type="number" v-model:value="max_size" /> {{ max_size }}</div>
</div>
</template>
<div id='Vue_SVG'></div>
<script>

const AREA_W = 333
const AREA_H = 111

class Point {
  constructor( _no ) {
    this.no = _no
    this.color_h = _no * 53 % 360
    this.x = Math.random() * AREA_W
    this.y = Math.random() * AREA_H
    this.movespeed_x = (Math.random() - 0.5)
    this.movespeed_y = (Math.random() - 0.5)
  }
  move() {
    this.x += this.movespeed_x
    this.y += this.movespeed_y
    if ( this.x < 0      ) { this.x = 0; this.movespeed_x *= -1 }
    if ( this.y < 0      ) { this.y = 0; this.movespeed_y *= -1 }
    if ( this.x > AREA_W ) { this.x = AREA_W; this.movespeed_x *= -1 }
    if ( this.y > AREA_H ) { this.y = AREA_H; this.movespeed_y *= -1 }
  }
}

var vm = new Vue( {
    el: 'div#Vue_SVG',
    template: document.querySelector('template#tags').innerHTML,
    data: {
        p_list:   [],
        p_size:   0,
        max_size: 200,
    },
} )

setInterval( ()=>
{
    if ( vm.p_size != vm.max_size ) { // 再構築
        vm.p_list = []
        for ( let i=0 ; i<vm.max_size ; ++i )
            vm.p_list.push( new Point( i+1 ) )
        vm.p_size = vm.max_size
    }
    for ( let p of vm.p_list ) p.move()  // 移動

}, 1000/60 ) // 60fps
</script>
```

<p class="codepen" data-height="425" data-theme-id="dark" data-default-tab="html,result" data-user="yamazaki3104" data-slug-hash="LYGgGGz" style="height: 425px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js 2.6.11 SVG example "></p>

## 2-1, コード量のまとめ

コード量を行数でソートすると以下の順になりました。

・(1) AngularJS 1.8.0 のコード(73行)
・(2) Vue.js 2.6.11 のコード(75行)
・(3) KnockoutJS 3.5.1 のコード(82行)
・(4) React 16.13.1 のコード(97行)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/bfba33ef-35e0-a8d7-aa21-ec28ae506ae1.png)

[Angular] が意外にもすんなり書けていて、好感触👍
[knockoutjs] はもうちょっと短く書けるかもしれません🙇‍♂️

全体としては、[React] のハードルが高めであることが再確認された感触です。

（コード量を「行数」のようなアバウトな定義で数値化することに抵抗を感じるひともおられると思いますが、そもそも課題を選ぶ時点で向き不向きもありコードへの影響もあります。コードの読みやすさ、書きやすさといった感覚もアバウトですし、この行数も、その程度のアバウトな数値であると理解していただきたいと思います。よろしくお願いいたします。）

### 2-2, データバインディング

ところで、４つのUIライブラリで書いてみて、大きな違いに気がついたのですが、データのバインディングのされ方に違いが見られました。データをどのように監視するか？といった思想的な仕様の部分です。

たとえば、[Vue.js] は setInterval() などで、外部からデータを非同期的に更新しても、なんの問題もなく表示が追従するのですが、
[React] は明示的に、setState() で更新したことを知らせる必要がありました。[knockoutjs] も [React] と同じように更新を明示しないとデータの内部までは監視していない動きでした（書き方に注意が必要なのかもしれない）。
[Angular] は [Vue.js] と同じように非同期の更新に追従して表示されますが、スコープの外からデータを更新できるのか？までは不明でした。

ですので、データバインディングという観点からは [Vue.js] が一番素直な動きだったので、書きやすかったです。

## 2-3, UIライブラリそのもののサイズ

念の為、JSファイルの byte 数も調べて記録しておきます。それぞれのUIライブラリの JSファイルをダウンロードして byte 数を調べました。

```
angular.min.js	172 KB (176,531バイト)

knockout-latest.min.js	65.6 KB (67,224 バイト)

react.production.min.js	12.1 KB (12,463 バイト)
react-dom.production.min.js	115 KB (118,656 バイト)
babel.min.js	772 KB (791,236 バイト)

vue.min.js	91.4 KB (93,670 バイト)
```

表にするとよくわかりますが、[React] がダントツです。babel が足かせになっていますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/60a44d32-b13e-1872-2402-19d062e41eaa.png)

[Vue.js] が小さいと思っていましたが、実際に一番小さかったのは [knockoutjs] でした。

ファイルが小さい方がネットに負荷を与えず素早く起動できそうです。しかし、ネットが高速になった昨今ではあまり気にしなくてよいのかもしれません。

# 3. メモリ使用量

Chrome の DevTool を開いて、Memory タブの 「Allocation instrumentation on timeline」を使って、20秒間計測し、結果を「statistics」で表示し、キャプチャしました。

キャプチャには fps も表示されていますが、DevTool の Rendering タブの「FPS meter」をONにして表示しています。

上で書いた４つのコードをそれぞれ Chrome で実行し、表示する object の数を 1, 10, 100, 1000, 10000 と変えて、メモリ使用量を確認しました。 

## まずは、Object １つから。

- [Angular] 1.8.0 -> 1873KB
![2020-07-21--AN-1-1873KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/bf068335-88aa-2e0e-13a7-738ca44de3b3.png)

- [knockoutjs] 3.5.1 -> 1845KB
![2020-07-21--KO-1-1845KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/61462f9a-8eaf-cfbf-0f3e-9921e6333b70.png)

- [React] 16.13.1 -> 3153KB
![2020-07-21--Re-1-3153KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/bdef19e9-0190-ef49-b4a0-a3a71fb51fd6.png)

- [Vue.js] 2.6.11 -> 1681KB
![2020-07-21--Vu-1-1681KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/ae84a6ab-ccb8-fb23-a8fd-ad4771023039.png)


[React] だけ 3MB 超えていますが、babel を使っているので仕方がないかと。
他の３つは同じくらい。[Vue.js] は軽いイメージの通りで、[React] も重いイメージの通り。
[knockoutjs] は、まぁ、そのくらいかなぁという印象で。
[Angular] は予想に反して軽め、ちょっと予想外。


## Object 10 個。

- [Angular] 1.8.0 -> 1952KB
![2020-07-21--AN-10-1952KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/40b54ff8-eab4-1d19-d699-7b49bc8d98a6.png)

- [knockoutjs] 3.5.1 -> 1915KB
![2020-07-21--KO-10-1915KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/abc85037-dcc1-26de-5d31-b07fc2d76a88.png)

- [React] 16.13.1 -> 3265KB
![2020-07-21--Re-10-3265KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/5b362eb7-275b-c611-c0c5-82fe9a6d1ef6.png)

- [Vue.js] 2.6.11 -> 1787KB
![2020-07-21--Vu-10-1787KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/a7238509-4ddb-f1bb-5db0-072645d07d69.png)

予想通り、object 1つのときとあまり変わらないですね。変わったら怖いですけど。

## Object 100 個。

- [Angular] 1.8.0 -> 2493KB
![2020-07-21--AN-100-2493KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/edc33e61-1748-ffa8-ced5-9c09cebbc9ce.png)


- [knockoutjs] 3.5.1 -> 2109KB
![2020-07-21--KO-100-2109KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/dc87b1cc-f2b1-1d22-1ac7-93b94ad49739.png)


- [React] 16.13.1 -> 3586KB 
![2020-07-21--Re-100-3586KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/0c6c9c78-28d9-6870-7130-1c0ae0ad58b9.png)


- [Vue.js] 2.6.11 -> 2189KB
![2020-07-21--Vu-100-2189KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/f56ae24d-fc68-3359-0ec7-1cfd762c3f36.png)

さすがに、object 100個になると使用するメモリも増え始めます。


## Object 1,000 個。

- [Angular] 1.8.0 -> 7806KB
![2020-07-21--AN-1000-7806KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/e4d1c840-79e0-f5a3-2df4-591d329181f8.png)


- [knockoutjs] 3.5.1 -> 3677KB 
![2020-07-21--KO-1000-3677KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/5ac2a875-7f50-6998-0160-faf937cd913b.png)


- [React] 16.13.1 -> 6671KB
![2020-07-21--Re-1000-6671KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/2678747f-0f9f-7d52-1e8a-651b8c0b5de2.png)


- [Vue.js] 2.6.11 -> 5977KB
![2020-07-21--Vu-1000-5977KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/a601aa75-fc37-8a25-8a41-c081ccca23fa.png)

object 1000 個は、なかなか厳しいのではないでしょうか。object も描ききれず、背景が隠れています。
[Angular] のメモリ使用量が増えてきました。ここで、[React]を抜いてトップです。


## Object 10,000 個。😨

- [Angular] 1.8.0 -> 60037KB
![2020-07-21--AN-10000-60037KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/acc25fb1-a438-3714-0ab2-8d9590bf60ee.png)


- [knockoutjs] 3.5.1
計測不能（残念）ってか、他の３つがすごすぎ。

- [React] 16.13.1 -> 37354KB
![2020-07-21--Re-10000-37354KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/463ffaa7-c5d8-3780-122b-2d24f3407edd.png)


- [Vue.js] 2.6.11 -> 42547KB
![2020-07-21--Vu-10000-42547KB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/42ce310f-ac76-27d9-abe9-be74cff7e27d.png)

さて、無謀な object１万個ですが、なんと[Angular], [React], [Vue.js] は 1fps をキープして動いています。すごいですね。
メモリ使用量は、[Angular] が 60MB と、ダントツですが。
[Vue.js]も[React]を抜いて２位の 42MB になりました。


## 3-1. メモリ使用量のまとめ

まとめると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/56e70928-2fa2-51ef-4dd3-2ea077c2e3c9.png)

軽いのは、最初は[Vue.js]で、途中は[knockoutjs]、最後には[React] になる波乱の展開でした。メモリ対決で[knockoutjs]が善戦してくるのは意外でしたが、１万個が計測できないのはわたしの書いたコードが悪いのだと思います。すみません。動いたらもしかするともしかするかも、、。

# 4. 速度(fps)

適度に負荷のかかっている、object 1000 個のときの、fps で比較しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/107592/e718b483-e06d-c679-b5bb-f0f92b4a99ef.png)

ズバリ、もっとも早いのは、[Angular]！！これは意外（失礼）

なんでみんな使わないの？？

[React] も早いけど、[Vue.js]のほうが早いと思っていた。速度に関しては、予想と逆の結果が出ました。

[knockoutjs]は、ごめんなさい。たぶん、わたしの書き方がいけないんです。


# 5. まとめ

調べてみるといろいろ予想外なことがありました（エビデンス重要😜）。

コードの書きやすさ、ファイルの軽さで選ぶなら、[Vue.js] か [knockoutjs] あたり。

処理速度で選ぶなら（どれも頑張っているが） [Angular] が早い。が、メモリは食う。

メモリ使用量で選ぶなら [Vue.js] か [knockoutjs] 。

で、「結局のところ、なにを使うのがいいの？」の答えは「どれもいいんじゃない？」でした。

意外にも数値にあまり差がみられなかった（みなさんがんばっているのですね、もっと差があると思っていました）ので、書きやすさ、読みやすさ、メンテのしやすさで選ぶとよいかと。

個人的には「[Vue.js]すげー！ 流石だね👍」と言って締めたかったのだけど、逆に、「[knockoutjs] や [Angular] もすごいじゃん、なんでみんな使わないの？」って思った。ってか、[React] をすすめるひとが多いように感じていたけど、どのあたりにメリットを感じてすすめているのかわからなくなった。盲目的に [React] 一択にするのはちょっと考えたほうがよさそう。

まとめると。UIライブラリは「いまのところ好みで選んで良し！数値から結論は出ない！」以上。😁

# 最後に

最後まで読んでいただきましてありがとうございます。結論が出なくて申し訳ありません。🙇‍♂️

UIライブラリ界隈の進化は凄まじく、まさに日進月歩。来月にはあたらしいライブラリが台頭してくるかもしれません。
そんな状況のなか、2020年7月の瞬間風速を記録しておく価値を感じたので、このエントリを書かせていただきました。

おそらく、見落としや勘違いもあると思います。すべてはわたしの力不足が原因でございます。あたたかい眼差しでコメントをいただけますと幸いです。

みなさまの快適なプログラミングライフを願いつつ、失礼します。