Skip to content

👁️ usePageVisibility - ページ可視性検知器

🌟 紹介

あなたのウェブページが賢明なアシスタントのように、ユーザーがそれを見ているかどうかを感知できると想像してください。ユーザーが他のタブに切り替えたり、ウィンドウを最小化したり、ページから離れたりすると、usePageVisibility は親切な「目」のように、ページの可視状態をリアルタイムで教えてくれます!

この機能はユーザーエクスペリエンスの最適化に特に適しています:動画再生の一時停止、アニメーションの停止、リソース消費の節約、またはユーザーが戻ってきたときに操作を復元するなど。あなたのアプリケーションをよりインテリジェントかつ省エネにしましょう!

🚀 コードデモ

基本的な使い方 - ページ状態の監視

js
import { watch } from 'vue';
import { usePageVisibility } from '@vant/use';

export default {
  setup() {
    const pageVisibility = usePageVisibility();

    watch(pageVisibility, (value) => {
      if (value === 'visible') {
        console.log('👀 ユーザーが戻ってきました!ページが可視になりました');
      } else {
        console.log('😴 ユーザーが離れました、ページは非表示になりました');
      }
    });

    return {
      pageVisibility,
    };
  },
};

動画再生制御 - スマート一時停止

js
import { ref, watch } from 'vue';
import { usePageVisibility } from '@vant/use';

export default {
  setup() {
    const videoRef = ref();
    const pageVisibility = usePageVisibility();
    const wasPlaying = ref(false);

    watch(pageVisibility, (isVisible) => {
      if (!videoRef.value) return;

      if (isVisible === 'hidden') {
        // 📱 ページが非表示になったとき、再生状態を記録して一時停止
        wasPlaying.value = !videoRef.value.paused;
        if (wasPlaying.value) {
          videoRef.value.pause();
          console.log('🎬 ビデオは自動的に一時停止されました');
        }
      } else {
        // 👀 ページが可視になったとき、再生を再開
        if (wasPlaying.value) {
          videoRef.value.play();
          console.log('▶️ ビデオの再生を自動的に再開しました');
        }
      }
    });

    return {
      videoRef,
      pageVisibility,
    };
  },
};

动画控制 - 性能优化

js
import { ref, watch } from 'vue';
import { usePageVisibility } from '@vant/use';

export default {
  setup() {
    const pageVisibility = usePageVisibility();
    const animationId = ref(null);
    const isAnimating = ref(false);

    // 🎨 动画函数
    const startAnimation = () => {
      if (!isAnimating.value) return;
      
      // 执行动画逻辑
      console.log('🎭 动画帧更新');
      
      animationId.value = requestAnimationFrame(startAnimation);
    };

    // 📱 根据页面可见性控制动画
    watch(pageVisibility, (isVisible) => {
      if (isVisible === 'visible') {
        if (isAnimating.value && !animationId.value) {
          console.log('🎬 恢复动画');
          startAnimation();
        }
      } else {
        if (animationId.value) {
          console.log('⏸️ 暂停动画以节省性能');
          cancelAnimationFrame(animationId.value);
          animationId.value = null;
        }
      }
    });

    const toggleAnimation = () => {
      isAnimating.value = !isAnimating.value;
      if (isAnimating.value && pageVisibility.value === 'visible') {
        startAnimation();
      } else if (animationId.value) {
        cancelAnimationFrame(animationId.value);
        animationId.value = null;
      }
    };

    return {
      pageVisibility,
      isAnimating,
      toggleAnimation,
    };
  },
};

数据同步 - 智能刷新

js
import { ref, watch } from 'vue';
import { usePageVisibility } from '@vant/use';

export default {
  setup() {
    const pageVisibility = usePageVisibility();
    const data = ref([]);
    const lastUpdateTime = ref(Date.now());

    // 📊 获取数据的函数
    const fetchData = async () => {
      try {
        console.log('🔄 正在获取最新数据...');
        // 模拟 API 调用
        const response = await fetch('/api/data');
        data.value = await response.json();
        lastUpdateTime.value = Date.now();
        console.log('✅ データ更新が完了しました');
      } catch (error) {
        console.error('❌ データ取得に失敗しました:', error);
      }
    };

    // 👀 页面重新可见时检查是否需要刷新数据
    watch(pageVisibility, (isVisible) => {
      if (isVisible === 'visible') {
        const timeSinceLastUpdate = Date.now() - lastUpdateTime.value;
        const fiveMinutes = 5 * 60 * 1000;

        if (timeSinceLastUpdate > fiveMinutes) {
          console.log('⏰ データが期限切れのため自動更新');
          fetchData();
        } else {
          console.log('✨ データは新鮮で更新不要');
        }
      }
    });

    return {
      data,
      pageVisibility,
      fetchData,
    };
  },
};

用户行为统计 - 精准分析

js
import { ref, watch, onMounted, onUnmounted } from 'vue';
import { usePageVisibility } from '@vant/use';

export default {
  setup() {
    const pageVisibility = usePageVisibility();
    const sessionStartTime = ref(Date.now());
    const totalVisibleTime = ref(0);
    const currentSessionStart = ref(Date.now());

    // 📈 统计页面可见时间
    watch(pageVisibility, (isVisible) => {
      const now = Date.now();
      
      if (isVisible === 'visible') {
        console.log('👁️ ユーザーがページを閲覧を開始');
        currentSessionStart.value = now;
      } else {
        console.log('👋 ユーザーがページを離れました');
        const sessionDuration = now - currentSessionStart.value;
        totalVisibleTime.value += sessionDuration;
        
        // 📊 发送统计数据
        sendAnalytics({
          event: 'page_visibility_change',
          visible_duration: sessionDuration,
          total_visible_time: totalVisibleTime.value,
        });
      }
    });

    // 📤 发送统计数据
    const sendAnalytics = (data) => {
      console.log('📊 ユーザービヘイビアデータを送信:', data);
      // 实际项目中这里会调用分析服务
    };

    // 🔚 页面卸载时发送最终统计
    onUnmounted(() => {
      if (pageVisibility.value === 'visible') {
        const finalDuration = Date.now() - currentSessionStart.value;
        totalVisibleTime.value += finalDuration;
      }
      
      sendAnalytics({
        event: 'page_unload',
        total_session_time: Date.now() - sessionStartTime.value,
        total_visible_time: totalVisibleTime.value,
      });
    });

    return {
      pageVisibility,
      totalVisibleTime,
    };
  },
};

📚 API 参考

类型定义

ts
type VisibilityState = 'visible' | 'hidden';

function usePageVisibility(): Ref<VisibilityState>;

返回值

パラメータ説明タイプ
visibilityState🎯 ページの現在の可視状態、visible は可視、hidden は非可視Ref<VisibilityState>

状態説明

状態説明トリガーシーン
visible👀 ページが可視ページがフォアグラウンドにあり、ユーザーが閲覧中
hidden😴 ページが非可視タブの切り替え、ウィンドウの最小化、ロック画面など

🎯 実際の使用シナリオ

📱 モバイルアプリの最適化

js
// モバイル省電力モード
const usePowerSaving = () => {
  const pageVisibility = usePageVisibility();
  
  watch(pageVisibility, (isVisible) => {
    if (isVisible === 'hidden') {
      // リフレッシュレートを下げて、電力を節約
      document.body.classList.add('power-saving');
    } else {
      document.body.classList.remove('power-saving');
    }
  });
};

🎮 ゲームアプリ

js
// ゲームの自動一時停止
const useGamePause = () => {
  const pageVisibility = usePageVisibility();
  const gameState = ref('playing');
  
  watch(pageVisibility, (isVisible) => {
    if (isVisible === 'hidden' && gameState.value === 'playing') {
      gameState.value = 'paused';
      console.log('🎮 ゲームは自動的に一時停止されました');
    }
  });
  
  return { gameState };
};

💬 チャットアプリ

js
// メッセージ通知管理
const useMessageNotification = () => {
  const pageVisibility = usePageVisibility();
  const unreadCount = ref(0);
  
  watch(pageVisibility, (isVisible) => {
    if (isVisible === 'visible') {
      // 通知をクリアし、未読カウントをリセット
      unreadCount.value = 0;
      document.title = 'チャットアプリ';
    }
  });
  
  const addMessage = (message) => {
    if (pageVisibility.value === 'hidden') {
      unreadCount.value++;
      document.title = `(${unreadCount.value}) 新着メッセージ - チャットアプリ`;
    }
  };
  
  return { addMessage };
};

💡 ベストプラクティス

✅ 推奨される使い方

js
// 1. 他の Hook と組み合わせて使用
const useSmartTimer = () => {
  const pageVisibility = usePageVisibility();
  const { pause, resume } = useCountDown();
  
  watch(pageVisibility, (isVisible) => {
    isVisible === 'visible' ? resume() : pause();
  });
};

// 2. 頻繁な切り替えに対するデバウンス処理
const useStableVisibility = () => {
  const pageVisibility = usePageVisibility();
  const stableVisibility = ref(pageVisibility.value);
  
  watchDebounced(
    pageVisibility,
    (value) => {
      stableVisibility.value = value;
    },
    { debounce: 500 }
  );
  
  return stableVisibility;
};

❌ 避けるべき使い方

js
// ❌ 非表示時に重い操作を実行しないでください
watch(pageVisibility, (isVisible) => {
  if (isVisible === 'hidden') {
    // ページが非表示のときに複雑な計算を実行するのは避けてください
    heavyComputation();
  }
});

// ❌ タイマーのクリーンアップを忘れないでください
watch(pageVisibility, (isVisible) => {
  if (isVisible === 'visible') {
    setInterval(() => {
      // メモリリークの原因になる可能性があります
    }, 1000);
  }
});

🛠️ デバッグのヒント

状態モニタリング

js
// 詳細な状態ログを追加
const pageVisibility = usePageVisibility();

watch(pageVisibility, (isVisible, oldValue) => {
  console.log('📊 ページ可視性の変化:', {
    from: oldValue,
    to: isVisible,
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent,
  });
});

パフォーマンスモニタリング

js
// ページの非表示がパフォーマンスに与える影響をモニタリング
let performanceMarks = [];

watch(pageVisibility, (isVisible) => {
  const mark = {
    visibility: isVisible,
    timestamp: performance.now(),
    memory: performance.memory?.usedJSHeapSize,
  };
  
  performanceMarks.push(mark);
  console.log('⚡ パフォーマンスマーク:', mark);
});

🌐 ブラウザ互換性

ブラウザサポートバージョン説明
Chrome14+✅ 完全サポート
Firefox18+✅ 完全サポート
Safari7+✅ 完全サポート
Edge12+✅ 完全サポート
IE10+⚠️ 部分的なサポート

📚 関連ドキュメント

🎯 状態管理関連

📱 パフォーマンス最適化関連

🎨 ユーザーエクスペリエンス関連

🛠️ 開発ツール

📖 実践例

Vant に基づく企業向けモバイルソリューション