在 Vue3 项目中实现定时刷新 vxe-table 数据,并在页面不可见时暂停刷新

在 Vue3 项目中实现定时刷新 vxe-table 数据,并在页面不可见时暂停刷新,可以通过以下方案实现:

方案一:使用 Page Visibility API + setInterval

<template>
  <div>
    <vxe-table
      :data="tableData"
      :loading="loading"
    >
      <!-- 表格列配置 -->
      <vxe-column field="id" title="ID"></vxe-column>
      <vxe-column field="name" title="名称"></vxe-column>
      <vxe-column field="status" title="状态"></vxe-column>
    </vxe-table>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { VxeTable, VxeColumn } from 'vxe-table'

const tableData = ref([])
const loading = ref(false)
let refreshTimer = null
const refreshInterval = 10000 // 10秒

// 获取表格数据
const fetchData = async () => {
  try {
    loading.value = true
    // 模拟API请求
    const response = await fetch('/api/your-data-endpoint')
    const data = await response.json()
    tableData.value = data
  } catch (error) {
    console.error('获取数据失败:', error)
  } finally {
    loading.value = false
  }
}

// 启动定时刷新
const startAutoRefresh = () => {
  if (refreshTimer) {
    clearInterval(refreshTimer)
  }
  refreshTimer = setInterval(() => {
    if (!document.hidden) {
      fetchData()
    }
  }, refreshInterval)
}

// 停止定时刷新
const stopAutoRefresh = () => {
  if (refreshTimer) {
    clearInterval(refreshTimer)
    refreshTimer = null
  }
}

// 处理页面可见性变化
const handleVisibilityChange = () => {
  if (document.hidden) {
    // 页面不可见,停止刷新
    stopAutoRefresh()
  } else {
    // 页面可见,开始刷新
    startAutoRefresh()
    // 立即刷新一次数据
    fetchData()
  }
}

onMounted(() => {
  // 初始加载数据
  fetchData()
  
  // 启动定时刷新
  startAutoRefresh()
  
  // 监听页面可见性变化
  document.addEventListener('visibilitychange', handleVisibilityChange)
  
  // 监听页面卸载
  window.addEventListener('beforeunload', stopAutoRefresh)
})

onUnmounted(() => {
  // 清理定时器和监听器
  stopAutoRefresh()
  document.removeEventListener('visibilitychange', handleVisibilityChange)
  window.removeEventListener('beforeunload', stopAutoRefresh)
})
</script>

方案二:使用 Web Worker 实现更精确的定时

// utils/refreshWorker.js
let refreshTimer = null
let interval = 10000
let isPageVisible = true

self.onmessage = function(e) {
  const { type, data } = e.data
  
  switch (type) {
    case 'START':
      interval = data.interval || 10000
      startTimer()
      break
    case 'STOP':
      stopTimer()
      break
    case 'VISIBILITY_CHANGE':
      isPageVisible = data.visible
      if (isPageVisible && !refreshTimer) {
        startTimer()
      }
      break
    case 'UPDATE_INTERVAL':
      interval = data.interval
      if (refreshTimer) {
        stopTimer()
        startTimer()
      }
      break
  }
}

function startTimer() {
  if (refreshTimer) return
  
  refreshTimer = setInterval(() => {
    if (isPageVisible) {
      self.postMessage({ type: 'REFRESH' })
    }
  }, interval)
  
  // 立即触发一次
  if (isPageVisible) {
    self.postMessage({ type: 'REFRESH' })
  }
}

function stopTimer() {
  if (refreshTimer) {
    clearInterval(refreshTimer)
    refreshTimer = null
  }
}
<template>
  <div>
    <div class="refresh-controls">
      <span>刷新间隔:</span>
      <select v-model="selectedInterval" @change="updateInterval">
        <option :value="5000">5秒</option>
        <option :value="10000">10秒</option>
        <option :value="30000">30秒</option>
      </select>
      <button @click="toggleAutoRefresh">
        {{ autoRefreshEnabled ? '停止' : '开始' }}自动刷新
      </button>
    </div>
    
    <vxe-table
      :data="tableData"
      :loading="loading"
    >
      <!-- 表格列配置 -->
    </vxe-table>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'

const tableData = ref([])
const loading = ref(false)
const autoRefreshEnabled = ref(true)
const selectedInterval = ref(10000)

let refreshWorker = null

// 获取数据
const fetchData = async () => {
  try {
    loading.value = true
    // 这里替换为实际的API调用
    const response = await fetch('/api/data')
    const data = await response.json()
    tableData.value = data
  } catch (error) {
    console.error('获取数据失败:', error)
  } finally {
    loading.value = false
  }
}

// 初始化Web Worker
const initWorker = () => {
  if (typeof Worker !== 'undefined') {
    refreshWorker = new Worker(
      new URL('../utils/refreshWorker.js', import.meta.url)
    )
    
    refreshWorker.onmessage = (e) => {
      if (e.data.type === 'REFRESH') {
        fetchData()
      }
    }
    
    // 启动定时器
    refreshWorker.postMessage({
      type: 'START',
      data: { interval: selectedInterval.value }
    })
  }
}

// 更新刷新间隔
const updateInterval = () => {
  if (refreshWorker) {
    refreshWorker.postMessage({
      type: 'UPDATE_INTERVAL',
      data: { interval: selectedInterval.value }
    })
  }
}

// 切换自动刷新
const toggleAutoRefresh = () => {
  autoRefreshEnabled.value = !autoRefreshEnabled.value
  if (refreshWorker) {
    if (autoRefreshEnabled.value) {
      refreshWorker.postMessage({
        type: 'START',
        data: { interval: selectedInterval.value }
      })
    } else {
      refreshWorker.postMessage({ type: 'STOP' })
    }
  }
}

// 处理页面可见性
const handleVisibilityChange = () => {
  if (refreshWorker) {
    refreshWorker.postMessage({
      type: 'VISIBILITY_CHANGE',
      data: { visible: !document.hidden }
    })
  }
}

onMounted(() => {
  // 初始加载数据
  fetchData()
  
  // 初始化Web Worker
  initWorker()
  
  // 监听页面可见性变化
  document.addEventListener('visibilitychange', handleVisibilityChange)
  
  // 监听窗口聚焦事件(可选)
  window.addEventListener('focus', () => {
    if (refreshWorker && autoRefreshEnabled.value) {
      refreshWorker.postMessage({
        type: 'VISIBILITY_CHANGE',
        data: { visible: true }
      })
    }
  })
})

onUnmounted(() => {
  // 清理
  if (refreshWorker) {
    refreshWorker.terminate()
    refreshWorker = null
  }
  document.removeEventListener('visibilitychange', handleVisibilityChange)
})
</script>

方案三:使用 Vue Composition API 封装为可复用 Hook

// composables/useAutoRefresh.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useAutoRefresh(callback, options = {}) {
  const {
    interval = 10000,
    immediate = true,
    autoStart = true
  } = options

  const isActive = ref(autoStart)
  const isPageVisible = ref(!document.hidden)
  let timer = null

  // 启动刷新
  const start = () => {
    if (timer) return
    isActive.value = true
    
    const executeRefresh = () => {
      if (isActive.value && isPageVisible.value) {
        callback()
      }
    }
    
    // 立即执行一次
    if (immediate) {
      executeRefresh()
    }
    
    // 设置定时器
    timer = setInterval(executeRefresh, interval)
  }

  // 停止刷新
  const stop = () => {
    isActive.value = false
    if (timer) {
      clearInterval(timer)
      timer = null
    }
  }

  // 处理页面可见性变化
  const handleVisibilityChange = () => {
    isPageVisible.value = !document.hidden
  }

  // 更新间隔时间
  const updateInterval = (newInterval) => {
    stop()
    options.interval = newInterval
    if (isActive.value) {
      start()
    }
  }

  onMounted(() => {
    // 监听页面可见性变化
    document.addEventListener('visibilitychange', handleVisibilityChange)
    
    if (autoStart) {
      start()
    }
  })

  onUnmounted(() => {
    stop()
    document.removeEventListener('visibilitychange', handleVisibilityChange)
  })

  return {
    isActive,
    start,
    stop,
    updateInterval
  }
}
<template>
  <div>
    <div class="control-panel">
      <button @click="toggleAutoRefresh">
        {{ autoRefreshControls.isActive ? '暂停' : '继续' }}自动刷新
      </button>
      <span>最后刷新时间: {{ lastRefreshTime }}</span>
    </div>

    <vxe-table :data="tableData">
      <!-- 表格列 -->
    </vxe-table>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { useAutoRefresh } from '@/composables/useAutoRefresh'

const tableData = ref([])
const lastRefreshTime = ref(null)

// 获取数据
const fetchTableData = async () => {
  try {
    const response = await fetch('/api/table-data')
    tableData.value = await response.json()
    lastRefreshTime.value = new Date().toLocaleTimeString()
  } catch (error) {
    console.error('刷新数据失败:', error)
  }
}

// 使用自动刷新Hook
const autoRefreshControls = useAutoRefresh(fetchTableData, {
  interval: 10000,
  immediate: true,
  autoStart: true
})

// 切换自动刷新
const toggleAutoRefresh = () => {
  if (autoRefreshControls.isActive) {
    autoRefreshControls.stop()
  } else {
    autoRefreshControls.start()
  }
}
</script>

关键点总结

  1. 页面可见性检测:使用 document.hiddenvisibilitychange 事件
  2. 资源清理:在组件卸载时清除定时器和事件监听器
  3. 错误处理:在数据刷新时添加适当的错误处理
  4. 用户体验:提供手动控制自动刷新的开关
  5. 性能优化:页面不可见时停止定时器,减少不必要的请求

建议使用方案一作为基础实现,如果需要更复杂的控制或复用逻辑,可以考虑使用方案三的 Composition API 封装。

posted @ 2025-12-15 09:04  dirgo  阅读(4)  评论(0)    收藏  举报