この記事は最終更新日から1年以上が経過しています。
@programming
投稿日 2019/12/23
更新日 2019/12/23 ✏
Vue.jsで大量の配列要素を滑らかに描画
Vue.jsで大量の配列データの要素を滑らかにレンダリングする方法です。
Vueモデルで管理され画面表示にも使われている配列データにforループ等で要素データを投入すると、Vueにレンダリング処理が移るまでイベントループがブロッキングされてしまい、画面がプチフリーズしてしまいます。 これを防ぐ方法は、setTimeout
等で小まめにイベントループを進めながら描画処理にも処理の機会を提供してあげることです。
目次
サンプルコード
以下はサンプルコードです。
...
<head>
...
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
</head>
...
<div id="content">
<button @click="badRender()">UIがプチフリーズするレンダリング</button>
<button @click="goodRender()">滑らかなレンダリング(ただし遅い)</button>
<button @click="betterRender()">速くて滑らかなレンダリング</button>
<div v-for="item in list">
<div v-text="item"></div>
</div>
</div>
<script type="text/javascript">
new Vue({
el: "#content",
data: {
list: [],
},
methods: {
getDummyList() {
const list = [];
for (let len = 9999, i = 0; i < len; i++) {
list.push(i);
}
return list;
},
// anti-Pattern! this logic causes ui blocking:
badRender() {
const self = this;
self.list = [];
const list = self.getDummyList();
for (let len = list.length, i = 0; i < len; i++) {
self.list.push(list[i]);
}
console.log("rendering done!");
},
// smooth (but slow) rendering:
goodRender() {
const self = this;
self.list = [];
const list = self.getDummyList();
const ite = function*() {
// NOTE: 1アイテムづつsetTimeoutでレンダリング
for (let len = list.length, i = 0; i < len; i++) {
yield setTimeout(() => {
const item = list[i]; // Get items one by one
self.list.push(item);
ite.next();
});
}
console.log("rendering done!");
}();
ite.next();
},
// smooth & faster rendering:
betterRender() {
const self = this;
self.list = [];
const list = self.getDummyList();
const ite = function*() {
// NOTE: 100アイテムづつsetTimeoutでレンダリング
while (true) {
const items = list.splice(0, 100); // Get items 100 by 100
if (items.length <= 0) break;
yield setTimeout(() => {
for (let len = items.length, i = 0; i < len; i++) {
const item = items[i]
self.list.push(item);
}
ite.next();
});
}
console.log("rendering done!");
}();
ite.next();
},
}
});
</script>
サンプルコードレビュー
全てのボタンの処理内容は全て同じです。 ボタンを押下すると、1〜9999までのデータがVueデータlist
にプッシュされ、画面にはその内容がレンダリングされます。 それぞれのボタンで異なるのは処理の仕方です。それぞれの処理の仕方はVueのmethods
に定義されています:
badRender()
:アンチパターンです。アイテムを一つづつVueデータlist
にプッシュしますが、次のイベントタイミングへ移る前にまとめてプッシュしているため、listのデータ量が多い場合にUIをプチフリーズさせてしまいます。goodRender()
: この方法はアイテムを一つづつアイテムを取得し、その都度setTimeout
でイベントループを進めながらVueデータlist
へプッシュしています。そのため、UIのプチフリーズを防ぎ、UIを滑らかに表示します。betterRender()
: この方法はsetTimeout
でイベントループを進めながら100アイテムづつVueデータlist
へプッシュしています。UIをそこそこ滑らかに表示しつつ、表示完了もgoodRender()
より早めることができます。
1番目の方法は非推奨のアンチパターンです。大量データのレンダリングではUIがプチフリーズするためです。 2番目の方法はそこそこオススメですが、ただし表示が全て完了するのにかなり時間が掛かります。UIを滑らかに表示しつつ、表示も速く完了させたいなら3番目の方法(1番目と2番目の折衷案)がベストです。
以上です。