Vueで大規模コンポーネントの遅延描画を実現し、画面遷移のパフォーマンスを改善

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描画を遅らせる時間(ミリ秒)Number10任意
forceRendertrueにすると即時描画トリガー。同一ルート内で再描画が必要な場合に利用Booleanfalse任意
sourceList監視対象の配列。変化時に条件付きで再描画Array-任意
watchSource配列監視モード。true時、ルート変更では再描画しないBooleanfalse任意
threshold遅延描画を有効にする要素数の閾値Number30任意
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の導入により、より柔軟な制御が可能になりました。

タグ: vue.js Performance Optimization Component Rendering

5月19日 17:54 投稿