- 投稿日:2020-07-21T19:24:15+09:00
Angular, knockoutjs, React, Vue.js の使用感とメモリ使用量と速度の記録(2020年7月)
いわゆるUIライブラリ(Angular, knockoutjs, React, Vue.js)の使用感とメモリ使用量と速度の記録(2020年7月)
プログラマのみなさまこんにちは?。ハーツテクノロジーの山崎です。この記事は仕事の中で得たられた知見によって書かれています。
で、いきなり冒頭から脱線してお詫びから入りますが、「UIライブラリ」という言葉はそのものズバリではないですよね。すみません。
「UIライブラリ」と書くと Bootstrap のようなCSS系ライブラリがメインのような気がしますし、「JSライブラリ」と書くと、広すぎて、UIとは無関係のライブラリが含まれてしまいます。「フレームワーク」も同様に広すぎるし、なんというか、データバインド系のJSライブラリの総称があるとよいのですが、いまのところ見つけられないので「UIライブラリ」を使わせていただいています。ピッタリな言葉がありましたらコメントください。よろしくお願いいたします。?♂️0. 背景「UIライブラリをどういう基準で選んだらよいのだろうか??」
フロントエンドUIライブラリってたくさんあって、どれを、どういう基準で選んだらよいのだろうか? いろいろな意見があるけど「実際の数値はどうなんだろう?」と気になった。うわさや感覚だけではなく、実際の数値を見て判断材料にしたいと思った。
今回調べたUIライブラリとそのバージョン
- Angular 1.8.0
- knockoutjs 3.5.1
- React 16.13.1
- Vue.js 2.6.11
方針としては、同じ課題を4種類のUIライブラリで実装してみて、以下のポイントを比べてみることとした。
・コード量、書きやすさ、可読性。
・Chrome で起動したときの使用メモリ(KB)と速度(fps) 。Chrome 以外のブラウザでの調査は保留。コードはすべてまるっと CodePen に置いたので、気になるひとは各自の環境で実行してみてくださいませ。
で、なぜこの4つを選んだのか?というと、特に理由はなくて、なんとなく有名どころを順に選んでみました。
Riot.js が入っていないのは「その昔、Riot.js で SVG を表示しようとしてひどい目にあった記憶が蘇るから」という個人的な理由だったりします。いまは改善されているのでしょうか?(詳しい方のフォローのコメントをお待ちします?)
1. ミッション→各UIライブラリで table と svg をたくさん表示してみる?
なるべく、コンパクトでわかりやすく、処理性能がわかるような課題を選んだ。
先に、動いているところを見てもらったほうが理解が早いと思うので、アニ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 ) // 60fpsQiita を読んでいるひとには解説は不要なくらいシンプルに書いたつもり。
class Point に座標(x,y), 色(color_h), 移動速度(movespeed_x, movespeed_y)を持って。move() メソッドで座標を移動し、範囲(AREA_W, AREA_H)からはみ出さないように位置と速度を調整しています。
max_size が object 数の入力値で、 p_list に Point object を入れています。
最後に、setInterval() を使って、60fps で p_list 内のすべての object を移動します。
常に変化するデータ p_list を、どのくらいリアルタイムに表示できるか? また、どのくらいのコード量で記述&表現できるのか?を調べるのがこのエントリのミッションとなります。
この共通コードはおおむね 40行なので、表示のために増えたコード量が UIライブラリ特有の記述量と考えてよいはず。
(念の為に、以下に実行環境の情報を貼って置きます。基本、スルーで。)
使用した Chrome のバージョン
使用した PC のスペック
ちなみに、ディスプレイのリフレッシュレートは 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 を作り直しています。速度が出ないのはこの書き方も理由だと思っています。(詳しいひとがおられましたらコメントいただけるとたすかります。?)
書き方が、
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つのなかでは、比較的書きやすく、また読みやすくも感じました。ストレスが少ないです。
template:
を使ったので、HTML tag を文字列として指定したためシンタックスハイライト機能が働かないので、目視チェックで頑張りました。
<template>
タグを使えば回避できるかも?とちょっと思ったのだけど、動かす方を優先してしまいました。<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 --> <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: `<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>`, 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行)Angular が意外にもすんなり書けていて、好感触?
knockoutjs はもうちょっと短く書けるかもしれません?♂️全体としては、React のハードルが高めであることが再確認された感触です。
(コード量を「行数」のようなアバウトな定義で数値化することに抵抗を感じるひともおられると思いますが、そもそも課題を選ぶ時点で向き不向きもありコードへの影響もあります。コードの読みやすさ、書きやすさといった感覚もアバウトですし、この行数も、その程度のアバウトな数値であると理解していただきたいと思います。よろしくお願いいたします。)
2-2, データバインディング
ところで、4つの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 が足かせになっていますね。
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つから。
Angular 1.8.0 -> 1873KB
knockoutjs 3.5.1 -> 1845KB
React 16.13.1 -> 3153KB
Vue.js 2.6.11 -> 1681KB
React だけ 3MB 超えていますが、babel を使っているので仕方がないかと。
他の3つは同じくらい。Vue.js は軽いイメージの通りで、React も重いイメージの通り。
knockoutjs は、まぁ、そのくらいかなぁという印象で。
Angular は予想に反して軽め、ちょっと予想外。Object 10 個。
Angular 1.8.0 -> 1952KB
knockoutjs 3.5.1 -> 1915KB
React 16.13.1 -> 3265KB
Vue.js 2.6.11 -> 1787KB
予想通り、object 1つのときとあまり変わらないですね。変わったら怖いですけど。
Object 100 個。
Angular 1.8.0 -> 2493KB
knockoutjs 3.5.1 -> 2109KB
React 16.13.1 -> 3586KB
Vue.js 2.6.11 -> 2189KB
さすがに、object 100個になると使用するメモリも増え始めます。
Object 1,000 個。
Angular 1.8.0 -> 7806KB
knockoutjs 3.5.1 -> 3677KB
React 16.13.1 -> 6671KB
Vue.js 2.6.11 -> 5977KB
object 1000 個は、なかなか厳しいのではないでしょうか。object も描ききれず、背景が隠れています。
Angular のメモリ使用量が増えてきました。ここで、Reactを抜いてトップです。Object 10,000 個。?
Angular 1.8.0 -> 60037KB
knockoutjs 3.5.1
計測不能(残念)ってか、他の3つがすごすぎ。React 16.13.1 -> 37354KB
Vue.js 2.6.11 -> 42547KB
さて、無謀な object1万個ですが、なんとAngular, React, Vue.js は 1fps をキープして動いています。すごいですね。
メモリ使用量は、Angular が 60MB と、ダントツですが。
Vue.jsもReactを抜いて2位の 42MB になりました。3-1. メモリ使用量のまとめ
軽いのは、最初はVue.jsで、途中はknockoutjs、最後にはReact になる波乱の展開でした。メモリ対決でknockoutjsが善戦してくるのは意外でしたが、1万個が計測できないのはわたしの書いたコードが悪いのだと思います。すみません。動いたらもしかするともしかするかも、、。
4. 速度(fps)
適度に負荷のかかっている、object 1000 個のときの、fps で比較しています。
ズバリ、もっとも早いのは、Angular!!これは意外(失礼)
なんでみんな使わないの??
React も早いけど、Vue.jsのほうが早いと思っていた。速度に関しては、予想と逆の結果が出ました。
knockoutjsは、ごめんなさい。たぶん、わたしの書き方がいけないんです。
5. まとめ
調べてみるといろいろ予想外なことがありました(エビデンス重要?)。
コードの書きやすさ、ファイルの軽さで選ぶなら、Vue.js か knockoutjs あたり。
処理速度で選ぶなら(どれも頑張っているが) Angular が早い。が、メモリは食う。
メモリ使用量で選ぶなら Vue.js か knockoutjs 。
で、「結局のところ、なにを使うのがいいの?」の答えは「どれもいいんじゃない?」でした。
意外にも数値にあまり差がみられなかった(みなさんがんばっているのですね、もっと差があると思っていました)ので、書きやすさ、読みやすさ、メンテのしやすさで選ぶとよいかと。
個人的には「Vue.jsすげー! 流石だね?」と言って締めたかったのだけど、逆に、「knockoutjs や Angular もすごいじゃん、なんでみんな使わないの?」って思った。ってか、React をすすめるひとが多いように感じていたけど、どのあたりにメリットを感じてすすめているのかわからなくなった。盲目的に React 一択にするのはちょっと考えたほうがよさそう。
まとめると。UIライブラリは「いまのところ好みで選んで良し!数値から結論は出ない!」以上。?
最後に
最後まで読んでいただきましてありがとうございます。結論が出なくて申し訳ありません。?♂️
UIライブラリ界隈の進化は凄まじく、まさに日進月歩。来月にはあたらしいライブラリが台頭してくるかもしれません。
そんな状況のなか、2020年7月の瞬間風速を記録しておく価値を感じたので、このエントリを書かせていただきました。おそらく、見落としや勘違いもあると思います。すべてはわたしの力不足が原因でございます。あたたかい眼差しでコメントをいただけますと幸いです。
みなさまの快適なプログラミングライフを願いつつ、失礼します。
- 投稿日:2020-07-21T12:02:26+09:00
VSCodeのRemote-ContainerでReact(Node.js+TypeScript)環境をサクッと構築する。
環境構築はDockerで作りたい。
Dockerに慣れてくると何でもDockerを使いたくなりませか?
僕はそんな人間の一人で、パソコンを買い換えてDockerを本格的に使い始めてから、手元の環境が汚れるのが嫌で環境構築は全てDockerを使っています。潔癖症かな?今回は、VSCodeでDockerを使い倒すための神拡張機能のRemote-Containersを使って、JavaScriptのライブラリのReactの開発環境を作っていきます。
事前に準備が必要なもの
- Docker
- VSCode
Remote-Containersを使って環境を作る
事前にDockerがインストールされているものとします。
拡張機能のDockerとRemote-Containers をインストールします。
適当なフォルダを開いて、コマンドパレットから
Remote-Containers:Add Development Containers Configuration Files
を選択。
インストールしたい環境のサンプルが表示されるので、Show All Definitionsを選択し、
最新版であるNode.js 14 & TypeScriptを選択。
そうすると.devcontainerフォルダが生成され、中にはMS社が用意してくれた指定した環境のDockerFileと追加設定を行うdevcontainer.jsonが保存されています。
Reactを利用するにはコマンドラインツールのcreate-react-appを使うのがラクチンなので、これをnpmでインストールする設定をDockerFileに追記しておきます。
また、コンテナ側の3000番ポートを使用することになるので、devcontinaer.jsonファイルにフォワードポートの設定を追記します。
最後にこれらのファイルを元にDockerイメージとコンテナを生成します。拡張機能からRemote-Containers:Rebuild and Reopen in Container 実行。
(または右下に出てるであろうポップアップをクリック)
画面が一度閉じ、再び開いたあと少し待つと、VScodeの左下がコンテナの中に入っていることを示す状態に変わっていると思います。
これでReactの環境は完成です。簡単!
実際に動かしてみる
create-react-appを実際に使って、サンプルアプリを動かしてみます。
ターミナルを開いて、
# npx create-react-app test
を実行。必要なライブラリ一式がダウンロードされていきます。
Happy hacking!の文字が出たら完了です。
testディレクトリが作成されており、中にはReactを動かすためのテンプレート になるファイルが生成されています。
こいつを動かしてみます。
# cd/test //カレントディレクトリを移動 # npm start //アプリを起動アプリを起動したらlocalhost:3000にアクセス。下記のような画面が表示されていたら成功です!
コンテナから抜けるには、VSCodeの画面を閉じるだけでコンテナOKです。
再びコンテナに入るには、VSCodeのDockerのオプションからお目当てのコンテナを右クリックして、Startをクリック。
そうするとコンテナが立ち上がるので、もう一度右クリックしてAttach Visual Studio Codeをクリックして完了です。
それでは、良いDocker & Reactライフを!
- 投稿日:2020-07-21T11:27:41+09:00
typescript 基本的な型付け
基本的な型付け
TypeScriptはJavaScriptに対して型をつけるという方向で仕様が作られています。 JavaScriptは動的言語の中でも、いろいろ制約がゆるく、無名関数とオブジェクトを使ってかなり柔軟なプログラミングの手法を提供してきました。 そのため、オブジェクトに対して型をつける方法についても、他のJavaなどの静的型付け言語よりもかなり複雑な機能を持っています。
一番コスパのいい(手抜きな)型付け: any型
最初に、一番費用が少ない方法を紹介します。 それが any です。 any と書けば、TypeScriptのコンパイラは、その変数のチェックをすべて放棄します。
まずはコンパイルだけは通したい、というときに使うと良さそうです。: unknown型
anyと似ていますがタイプセーフな型です。型を特定するまであらゆる操作が制限されるみたいな型です。unknownは存在しうるすべての型の Union のようなものです。unknown型には任意の値を代入できる点はany型と同一ですが、型アサーション等が無いと利用できません。
まとめ
1:96スコープの中で最初からあるわけではないデータ
2:API をinterfaceで定義していく時に後回しにしたいプロパティの型
3:関数のオーバーロード定義時ただ引数の数を合わせたいだけの時の型
4:型の種類が多い(そして数が不明かつ API ドキュメントの無いプロジェクト)
みたいな所とかは一旦unknownで置いておいて、使う時になってから実際の型に置き換えたり、キャストしたりすると良い。
- 投稿日:2020-07-21T08:05:07+09:00
Reactのreturn内でコメントアウトする方法
- 投稿日:2020-07-21T03:18:12+09:00
Reactで複数の<input type="text">を扱う際のベストプラクティス
stateの定義
今回はfunctional component を使用しているためHookを使用してstateの定義をします。
const [state, setState] = useState({ hoge: "", fuga: "" })フォームの型
inputのnameを使用してstateを更新するため、nameはstateのkeyと同じにする必要があります。
<form onSubmit={e => handleSubmit(e)}> <input name="hoge" type="text" placeholder="Hoge" value={state.hoge} onChange={handleChange} /> <input name="fuga" type="text" placeholder="Fuga" value={state.fuga} onChange={handleChange} /> </form>フォーム入力時の関数
onChangeがトリガーされると、そのイベントを引数にとってhandleChangeが実行されます。
inputのnameをもとにsetStateを用いてkeyがnameであるstateのvalueを更新します。const handleChange = (e: any) => { const value = e.target.value; setState({ ...state, [e.target.name]: value }); }まとめ
export const Hoge: React.FC<Props> = (props: any) => { const [state, setState] = useState({ hoge: "", fuga: "" }) const handleChange = (e: any) => { const value = e.target.value; setState({ ...state, [e.target.name]: value }); } const handleSubmit = (e: any) => { e.preventDefault(); } return ( <div> <form onSubmit={e => handleSubmit(e)}> <input name="hoge" type="text" placeholder="Hoge" value={state.hoge} onChange={handleChange} /> <input name="fuga" type="text" placeholder="Fuga" value={state.fuga} onChange={handleChange} /> </form> </div> ); }