在 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 封裝。
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。