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構建的企業級移動端解決方案