👁️ 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);
});🌐 ブラウザ互換性
| ブラウザ | サポートバージョン | 説明 |
|---|---|---|
| Chrome | 14+ | ✅ 完全サポート |
| Firefox | 18+ | ✅ 完全サポート |
| Safari | 7+ | ✅ 完全サポート |
| Edge | 12+ | ✅ 完全サポート |
| IE | 10+ | ⚠️ 部分的なサポート |
📚 関連ドキュメント
🎯 状態管理関連
- useToggle - ブール値切り替え - 状態切り替え管理
- useEventListener - イベントリスナー - イベント処理
- useCountDown - カウントダウン - 時間管理
📱 パフォーマンス最適化関連
- useRaf - アニメーションフレーム - アニメーションパフォーマンス最適化
- useWindowSize - ウィンドウサイズ - レスポンシブ最適化
- useRect - 要素位置 - レイアウト最適化
🎨 ユーザーエクスペリエンス関連
- useClickAway - 外部クリック - インタラクティブエクスペリエンス
- useScrollParent - スクロールコンテナ - スクロール体験
🛠️ 開発ツール
- Vant Use 紹介 - コンポジション API ツールセット
- テーマカスタマイズ - スタイルテーマ設定