8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Angular, knockoutjs, React, Vue.js の使用感とメモリ使用量と速度の記録(2020年7月)

Last updated at Posted at 2020-07-22

いわゆるUIライブラリ(Angular, knockoutjs, React, Vue.js)の使用感とメモリ使用量と速度の記録(2020年7月)

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

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

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

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

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

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

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

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

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

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

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

なるべく、コンパクトでわかりやすく、処理性能がわかるような課題を選んだ。
先に、動いているところを見てもらったほうが理解が早いと思うので、アニgifを貼ります。こんな感じ👇
2020-07-22.gif

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

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

使用した PC のスペック

image.png

image.png

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

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

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

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

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

Angular 1.8.0 のコード(73行)

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

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

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

<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>

knockoutjs 3.5.1 のコード(82行)

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

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

<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>

React 16.13.1 のコード(97行)

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

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

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

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

<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>

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

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

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

<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>

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

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

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

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

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

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

たとえば、Vue.js は setInterval() などで、外部からデータを非同期的に更新しても、なんの問題もなく表示が追従するのですが、
React は明示的に、setState() で更新したことを知らせる必要がありました。knockoutjsReact と同じように更新を明示しないとデータの内部までは監視していない動きでした(書き方に注意が必要なのかもしれない)。
AngularVue.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

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

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

3. メモリ使用量

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

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

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

まずは、Object 1つから。

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

Object 10 個。

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

Object 100 個。

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

Object 1,000 個。

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

Object 10,000 個。😨

  • Angular 1.8.0 -> 60037KB
    2020-07-21--AN-10000-60037KB.png

  • knockoutjs 3.5.1
    計測不能(残念)ってか、他の3つがすごすぎ。

  • React 16.13.1 -> 37354KB
    2020-07-21--Re-10000-37354KB.png

  • Vue.js 2.6.11 -> 42547KB
    2020-07-21--Vu-10000-42547KB.png

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

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

まとめると
image.png

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

4. 速度(fps)

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

image.png

ズバリ、もっとも早いのは、Angular!!これは意外(失礼)

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

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

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

5. まとめ

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

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

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

メモリ使用量で選ぶなら Vue.jsknockoutjs

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

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

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

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

最後に

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

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

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

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

8
4
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?