👁️ 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 工具集
- 主题定制 - 样式主题配置