在 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>
关键点总结
- 页面可见性检测:使用
document.hidden和visibilitychange事件 - 资源清理:在组件卸载时清除定时器和事件监听器
- 错误处理:在数据刷新时添加适当的错误处理
- 用户体验:提供手动控制自动刷新的开关
- 性能优化:页面不可见时停止定时器,减少不必要的请求
建议使用方案一作为基础实现,如果需要更复杂的控制或复用逻辑,可以考虑使用方案三的 Composition API 封装。

浙公网安备 33010602011771号