Vue.jsとElement UIを用いた開発中に、大量データを扱う画面で操作やページ切り替えが重くなる問題に直面しました。原因は各セルに付与したtooltipが多数のDOMノードを生成していたためでした。これを削除したところ、操作性は大幅に改善されました。
しかし、Vuexで管理しているデータ量が多い場合、ルートの再表示時に2〜3秒ほど描画が遅れるという新たな課題が発生。ユーザー体験を損なわないよう、「画面遷移が完了してから重いコンポーネントを描画する」方法が必要でした。
調査の結果、Vue 1.x時代のv-ifを利用した遅延描画手法を参考に、Vue 2.x向けにカスタムコンポーネントとして再設計。その成果がvue-lazy-renderです。
主な機能
- コンポーネントの描画を任意のタイミングで遅延可能
- 遅延時間を自由に設定
- 配列データの変化やサイズに基づいて自動的に遅延描画を制御
基本的な使用例
<lazy-render>
<heavy-component />
</lazy-render>
<lazy-render
:source-list="items"
:delay-ms="500"
:threshold="100"
watch-source>
<data-table :rows="items" />
</lazy-render>
内部実装のポイント
テンプレート構造
<div class="deferred-container">
<slot v-if="isReady" />
<div v-else :class="maskStyle || 'default-mask'">{{ message }}</div>
</div>
プロパティ定義
| プロパティ名 | 説明 | 型 | 初期値 | 必須 |
|---|---|---|---|---|
| delayMs | 描画を遅らせる時間(ミリ秒) | Number | 10 | 任意 |
| forceRender | trueにすると即時描画トリガー。同一ルート内で再描画が必要な場合に利用 | Boolean | false | 任意 |
| sourceList | 監視対象の配列。変化時に条件付きで再描画 | Array | - | 任意 |
| watchSource | 配列監視モード。true時、ルート変更では再描画しない | Boolean | false | 任意 |
| threshold | 遅延描画を有効にする要素数の閾値 | Number | 30 | 任意 |
| maskStyle | ローディング中のマスクCSSクラス | String | - | 任意 |
| message | ローディング中の表示テキスト | String | 「読み込み中...」 | 任意 |
コアメソッド
/**
* 条件に応じて描画を遅延させる
*/
triggerDeferredRender() {
const shouldDefer = !this.sourceList ||
(this.sourceList.length > this.threshold);
if (shouldDefer) {
this.scheduleRender();
} else {
this.isReady = true;
this.$emit('render-complete');
}
},
/**
* タイマーによる描画スケジューリング
*/
scheduleRender() {
this.isReady = false;
setTimeout(() => {
this.isReady = true;
this.$emit('render-complete');
}, this.delayMs);
}
ライフサイクルとウォッチャー
created() {
this.triggerDeferredRender();
},
watch: {
sourceList: {
handler() {
if (this.watchSource) this.triggerDeferredRender();
},
deep: true
},
$route() {
if (!this.watchSource) this.triggerDeferredRender();
},
forceRender(newVal) {
if (newVal) this.triggerDeferredRender();
}
}
この実装により、isReadyフラグがfalseの間はプレースホルダを表示し、指定時間後にコンポーネントを描画。描画完了時にはrender-completeイベントを発行します。watchSourceモードは特定のユースケース向けですが、forceRenderの導入により、より柔軟な制御が可能になりました。