業務用WEBアプリケーションの管理画面を開発しているなかで
ちょいちょい出てくる非機能要件として一覧表示に時間がかかりすぎる
というものがあります。
適切なページング処理や適切なSQLであれば基本的に問題になることは少ないです。
今回試したのがエクセルのように行ごとにたくさんの入力項目があり、
編集→保存みたいなことをする画面です。
同じデータ件数でもinputタグの個数がおおくなるとやたら画面表示が遅くなるので
下の方法を試しました。
①サーバサイドレンダリング(php)で全件表示
②フロントサイドレンダリング(vue.js)で全件表示
③フロントサイドレンダリング(vue.js)で逐次表示
いずれもローカルネットワークサーバへのアクセスなのでネットワークの通信時間はほぼ0として考えています。
①サーバサイドレンダリング(php)で全件表示
まずはシンプルにphpで1000行のテーブルを表示するのにどれくらいかかるか見てみました。
そのときのコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>リスト表示</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<table class="table table-bordered table-condensed">
<thead>
<tr>
<th>id</th>
<?php
for ($i = 1; $i <= 20; $i++) {
echo '<td>カラム' . $i . '</td>';
}
?>
</tr>
</thead>
<tbody>
<?php
for ($i = 1; $i <= 1000; $i++) {
echo '<tr>';
echo '<td>' . $i .'</td>';
for($j = 1; $j <= 20; $j++){
echo '<td><input type="text" value="xxxx' . $i . '"></td>';
}
echo '</tr>';
}
?>
</tbody>
</table>
</body>
</html>
表示された画面
比較
表示する件数1000件のとき
1行あたりのinputタグ数 | 表示までの時間 |
---|---|
10 | 2.13s |
20 | 3.96s |
30 | 6.33s |
※Chrome DevToolsのNetworkタブLoadの時間を見ています
インプットタグが増えると時間かかるなあということがわかりました。
②フロントサイドレンダリング(vue.js)で全件表示
vue.jsをつかってフロントサイドで画面表示を行った場合に変わるのか試しました。
そのときのコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>リスト表示</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/bootstrap.min.css">
</head>
<body>
<div id="app">
<table class="table table-bordered table-condensed" id="orderTable">
<thead>
<tr>
<th>id</th>
<th v-for="n in 20">カラム{{n}}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in orders">
<td>{{ item.id }}</td>
<td v-for="n in 20"><input type="text" v-model="item.code"></td>
</tr>
</tbody>
</table>
</div>
<script src="js/vue.min.js"></script>
<script src="js/list-script.js"></script>
</body>
</html>
const vm = new Vue({
el: '#app',
data: {
orders: [],
},
methods: {
get_order: function(){
for(var i = 1; i <= 1000; i++){
var order = {};
order['id'] = i;
order['code'] = "xxxx" + i;
this.$data.orders.push(order);
}
},
},
created: function(){
this.get_order();
}
});
表示された画面
見た目は①と同じなので省略します。
比較
表示する件数1000件のとき
1行あたりのinputタグ数 | 表示までの時間 |
---|---|
10 | 2.05s |
20 | 3.50s |
30 | 5.55s |
そんなにかわらないなあということがわかりました。
結局inputタグの数が増えるとブラウザで画面の生成をするのに時間がかかるのが問題で
フロントで作るか、サーバサイドで作るかは、そこまで大きく変化がみられませんでした。
③フロントサイドレンダリング(vue.js)で逐次表示
inputタグが増えてきた場合に全件を頑張って表示するのは諦めて見える部分だけ表示して、
残りは順次読み込む方法で体感を早くすることを考えました。
(よくある無限スクロールで表示すれば体感だけは早くなるだろうということで試してみました。)
体感が速ければ顧客からは文句を言われないですからね。
そのときのコード
②と同じなので省略
const vm = new Vue({
el: '#app',
data: {
orders: [],
},
methods: {
get_order: function(){
for(var i = 1; i <= 100; i++){
var order = {};
order['id'] = i;
order['code'] = "xxxx" + i;
this.$data.orders.push(order);
}
},
infiniteScroll: function(event){
var pos = orderTable.getBoundingClientRect().bottom;
if(orderTable.getBoundingClientRect().bottom < window.innerHeight){
if (this.$data.orders.length >= 1000) {
return;
}
var startIndex = this.$data.orders.length + 1;
var endIndex = this.$data.orders.length + 100;
for (var i = startIndex; i <= endIndex; i++) {
var order = {};
order['id'] = i;
order['code'] = "xxxx" + i;
this.$data.orders.push(order);
}
}
},
},
created: function(){
this.get_order();
window.addEventListener('scroll', this.infiniteScroll);
}
});
表示された画面
見た目は①と同じなので省略します。
比較
最初に表示する件数を変化させてみました。
いずれもページのしたまで行くと新たに100件を表示します。(今回は最大1000件)
1行あたりのinputタグ数30個のとき
はじめから表示する件数 | 表示までの時間 |
---|---|
1000 | 5.57s |
500 | 3.45s |
100 | 2.17s |
まとめ
inputタグがたくさんある一覧表示では順次読み込んで表示するというのが
やはり効果があることがわかりました。
SNSとかで画像が多く使われる場合には有効であることはわかっていましたが
inputタグなど画面描画に時間がかかるタグを含む場合には効果が期待できるようです。
個人的にはフロントエンドとサーバサイドでレンダリング箇所を変えると
もっと差がでるかと思っていましたがそうでもなかったです。
もし、他に有効な実装方法や比較方法をご存知の方はコメントをお願いいたします。