高性能计算

清晰的解决方案步骤

第一部分:不使用Web Worker的方案(简单直接)

步骤1:创建时间管理Store

文件路径: src/stores/timeStore.js

javascript 复制代码
import { defineStore } from 'pinia'
import { ref, computed, onUnmounted } from 'vue'

export const useTimeStore = defineStore('time', () => {
  // 初始持续时间(从路由参数传入,单位:秒)
  const initialDurationMs = ref(0)
  // 当前持续时间(毫秒)
  const currentDurationMs = ref(0)
  // 开始时间点(performance.now())
  const startTime = ref(null)
  
  // 定时器相关
  let rafId = null
  let lastVisibleTime = null
  
  // 初始化持续时间
  const initDuration = (initialSeconds) => {
    initialDurationMs.value = initialSeconds * 1000
    currentDurationMs.value = initialDurationMs.value
    startTime.value = performance.now()
    lastVisibleTime = startTime.value
    
    // 启动高性能计时器
    startCounting()
    
    // 监听页面可见性变化
    setupVisibilityListener()
  }
  
  // 使用requestAnimationFrame进行精确计时
  const startCounting = () => {
    if (rafId) cancelAnimationFrame(rafId)
    
    const update = () => {
      const now = performance.now()
      const elapsed = now - startTime.value
      currentDurationMs.value = initialDurationMs.value + elapsed
      lastVisibleTime = now
      rafId = requestAnimationFrame(update)
    }
    
    update()
  }
  
  // 处理页面可见性变化
  const setupVisibilityListener = () => {
    const handleVisibilityChange = () => {
      if (document.hidden) {
        // 页面进入后台,暂停计时器
        if (rafId) {
          cancelAnimationFrame(rafId)
          rafId = null
        }
      } else {
        // 页面回到前台,补偿后台时间并重启计时器
        if (lastVisibleTime && !rafId) {
          const now = performance.now()
          const timeInBackground = now - lastVisibleTime
          currentDurationMs.value += timeInBackground
          startTime.value += timeInBackground
          startCounting()
        }
      }
    }
    
    document.addEventListener('visibilitychange', handleVisibilityChange)
    
    // 返回清理函数
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange)
    }
  }
  
  // 格式化持续时间
  const formattedDuration = computed(() => {
    const totalSeconds = Math.floor(currentDurationMs.value / 1000)
    const hours = Math.floor(totalSeconds / 3600)
    const minutes = Math.floor((totalSeconds % 3600) / 60)
    const seconds = totalSeconds % 60
    
    return {
      hours: hours.toString().padStart(2, '0'),
      minutes: minutes.toString().padStart(2, '0'),
      seconds: seconds.toString().padStart(2, '0'),
      totalMs: currentDurationMs.value
    }
  })
  
  // 清理资源
  const cleanup = () => {
    if (rafId) {
      cancelAnimationFrame(rafId)
      rafId = null
    }
  }
  
  onUnmounted(() => {
    cleanup()
  })
  
  return {
    formattedDuration,
    initDuration,
    cleanup,
    currentDurationMs
  }
})
步骤2:创建右上角持续时间组件

文件路径: src/components/DurationDisplay/index.vue

vue 复制代码
<template>
  <div class="duration-display">
    <el-icon><Clock /></el-icon>
    <span class="label">持续时间:</span>
    <span class="time">{{ formattedDuration.hours }}:{{ formattedDuration.minutes }}:{{ formattedDuration.seconds }}</span>
  </div>
</template>

<script setup>
import { onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { useTimeStore } from '@/stores/timeStore'
import { Clock } from '@element-plus/icons-vue'

// 获取路由参数中的初始时间
const route = useRoute()
const timeStore = useTimeStore()

// 从路由参数获取初始持续时间(单位:秒)
// 假设路由参数名为 initialDuration
const initialDuration = route.params.initialDuration || 
                       route.query.initialDuration || 
                       0

onMounted(() => {
  // 初始化持续时间
  timeStore.initDuration(Number(initialDuration))
})

onUnmounted(() => {
  // 清理资源
  timeStore.cleanup()
})

const formattedDuration = timeStore.formattedDuration
</script>

<style scoped>
.duration-display {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: 'Courier New', monospace;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 8px 16px;
  border-radius: 20px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

.duration-display .el-icon {
  font-size: 18px;
}

.duration-display .label {
  font-weight: 500;
  opacity: 0.9;
}

.duration-display .time {
  font-weight: bold;
  font-size: 18px;
  letter-spacing: 1px;
}
</style>
步骤3:在页面中使用持续时间组件

文件路径: src/views/YourPage.vue

vue 复制代码
<template>
  <div class="page-container">
    <!-- 右上角固定位置 -->
    <div class="duration-container">
      <DurationDisplay />
    </div>
    
    <!-- 页面内容 -->
    <div class="page-content">
      <!-- 表格组件 -->
      <DataTable />
    </div>
  </div>
</template>

<script setup>
import DurationDisplay from '@/components/DurationDisplay/index.vue'
import DataTable from '@/components/DataTable/index.vue'
</script>

<style scoped>
.page-container {
  position: relative;
  min-height: 100vh;
  padding: 20px;
}

.duration-container {
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 1000;
}
</style>
步骤4:创建表格组件

文件路径: src/components/DataTable/index.vue

vue 复制代码
<template>
  <div class="data-table-container">
    <div class="table-header">
      <el-button 
        type="primary" 
        @click="loadData"
        :loading="loading"
        icon="Refresh"
      >
        刷新数据
      </el-button>
      
      <!-- 调试信息(仅开发环境显示) -->
      <div v-if="showDebugInfo" class="debug-info">
        <el-tag type="info">
          最后同步时间: {{ lastSyncTime || '未同步' }}
        </el-tag>
      </div>
    </div>
    
    <el-table 
      :data="tableData" 
      v-loading="loading"
      style="width: 100%"
      stripe
    >
      <el-table-column 
        prop="id" 
        label="ID" 
        width="80"
        align="center"
      />
      <el-table-column 
        prop="name" 
        label="名称" 
        min-width="120"
      />
      <el-table-column 
        prop="value" 
        label="数值" 
        width="100"
        align="right"
      />
      <el-table-column 
        prop="timestamp" 
        label="创建时间" 
        width="180"
      >
        <template #default="{ row }">
          {{ formatTime(row.timestamp) }}
        </template>
      </el-table-column>
      <el-table-column 
        label="操作" 
        width="120"
        align="center"
      >
        <template #default>
          <el-button size="small">查看</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'

const tableData = ref([])
const loading = ref(false)
const lastSyncTime = ref(null)

// 是否显示调试信息
const showDebugInfo = computed(() => {
  return import.meta.env.DEV
})

// 格式化时间(直接显示后端返回的时间)
const formatTime = (timestamp) => {
  if (!timestamp) return '-'
  const date = new Date(timestamp)
  return date.toLocaleString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    second: '2-digit',
    hour12: false
  })
}

// 加载数据
const loadData = async () => {
  loading.value = true
  try {
    // 模拟API调用
    const response = await fetch('/api/table-data', {
      method: 'GET',
      headers: {
        'Cache-Control': 'no-cache, no-store, must-revalidate',
        'Pragma': 'no-cache'
      }
    })
    
    const data = await response.json()
    tableData.value = data
    
    // 记录最后同步时间
    lastSyncTime.value = new Date().toLocaleTimeString()
    
    ElMessage.success('数据加载成功')
    
    // 在生产环境,可以记录时间同步日志
    if (!import.meta.env.DEV) {
      console.log('表格数据加载时间:', {
        clientTime: new Date().toISOString(),
        sampleDataTime: data[0]?.timestamp
      })
    }
    
  } catch (error) {
    console.error('加载数据失败:', error)
    ElMessage.error('加载数据失败')
  } finally {
    loading.value = false
  }
}

// 初始化加载数据
loadData()
</script>

<style scoped>
.data-table-container {
  background: white;
  border-radius: 8px;
  padding: 20px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

.table-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.debug-info {
  display: flex;
  gap: 10px;
}
</style>

第二部分:使用Web Worker的方案(高级,适合对精度要求极高的场景)

Web Worker文件结构
复制代码
src/
├── workers/
│   ├── duration.worker.js      # Worker主文件
│   └── duration.worker.helper.js # Worker工具函数
├── stores/
│   └── timeStore.worker.js     # 使用Worker的Store
└── components/
    └── DurationDisplay.worker.vue # 使用Worker的组件
步骤1:创建Web Worker文件

文件路径: src/workers/duration.worker.js

javascript 复制代码
// 高性能计时器,使用performance.now()确保精度
let startTime = null
let initialDuration = 0
let currentDuration = 0
let lastUpdateTime = null
let isRunning = true

// 消息处理
self.onmessage = function(e) {
  const { type, data } = e.data
  
  switch (type) {
    case 'INIT':
      startTime = performance.now()
      initialDuration = data.initialDuration || 0
      currentDuration = initialDuration
      lastUpdateTime = startTime
      startTimer()
      break
      
    case 'PAUSE':
      isRunning = false
      break
      
    case 'RESUME':
      isRunning = true
      if (lastUpdateTime) {
        // 补偿暂停期间的时间
        const now = performance.now()
        const elapsed = now - lastUpdateTime
        currentDuration += elapsed
        lastUpdateTime = now
      }
      break
      
    case 'ADJUST':
      // 调整时间(用于同步)
      if (data.adjustment) {
        currentDuration += data.adjustment
      }
      break
      
    case 'GET_CURRENT':
      // 返回当前持续时间
      self.postMessage({
        type: 'CURRENT_DURATION',
        duration: currentDuration
      })
      break
      
    case 'DESTROY':
      self.close()
      break
  }
}

// 使用requestAnimationFrame计时
function startTimer() {
  function update() {
    if (!isRunning || startTime === null) return
    
    const now = performance.now()
    currentDuration = initialDuration + (now - startTime)
    lastUpdateTime = now
    
    // 每秒更新一次(减少消息传递频率)
    self.postMessage({
      type: 'DURATION_UPDATE',
      duration: currentDuration,
      timestamp: Date.now()
    })
    
    // 继续下一帧
    self.requestAnimationFrame(update)
  }
  
  // 使用模拟的requestAnimationFrame
  self.requestAnimationFrame = self.requestAnimationFrame || 
    function(callback) {
      return setTimeout(() => {
        callback(performance.now())
      }, 1000 / 60) // 60fps
    }
  
  update()
}

// 错误处理
self.onerror = function(error) {
  console.error('Worker error:', error)
  self.postMessage({
    type: 'ERROR',
    error: error.message
  })
}
步骤2:创建使用Worker的Store

文件路径: src/stores/timeStore.worker.js

javascript 复制代码
import { defineStore } from 'pinia'
import { ref, computed, onUnmounted } from 'vue'

export const useTimeStoreWithWorker = defineStore('timeWorker', () => {
  const durationMs = ref(0)
  let worker = null
  let cleanupListeners = null
  
  // 初始化Worker
  const initWorker = () => {
    if (worker) return
    
    worker = new Worker(new URL('@/workers/duration.worker.js', import.meta.url), {
      type: 'module'
    })
    
    // 监听Worker消息
    worker.onmessage = (e) => {
      const { type, duration } = e.data
      if (type === 'DURATION_UPDATE') {
        durationMs.value = duration
      }
    }
    
    // 错误处理
    worker.onerror = (error) => {
      console.error('Worker error:', error)
      // 降级为不使用Worker的计时
      fallbackToMainThread()
    }
    
    // 设置清理监听器
    cleanupListeners = setupVisibilityListener()
  }
  
  // 初始化持续时间
  const initDuration = (initialSeconds) => {
    initWorker()
    
    worker.postMessage({
      type: 'INIT',
      data: {
        initialDuration: initialSeconds * 1000
      }
    })
  }
  
  // 处理页面可见性变化
  const setupVisibilityListener = () => {
    const handleVisibilityChange = () => {
      if (!worker) return
      
      if (document.hidden) {
        // 页面进入后台,暂停Worker计时
        worker.postMessage({ type: 'PAUSE' })
      } else {
        // 页面回到前台,恢复Worker计时
        worker.postMessage({ type: 'RESUME' })
      }
    }
    
    document.addEventListener('visibilitychange', handleVisibilityChange)
    
    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange)
    }
  }
  
  // 降级方案:如果Worker不可用,使用主线程计时
  const fallbackToMainThread = () => {
    if (worker) {
      worker.terminate()
      worker = null
    }
    
    console.warn('Worker不可用,降级为主线程计时')
    
    // 这里可以调用不使用Worker的Store
    // 在实际项目中,你可能需要导入并切换store
  }
  
  // 格式化持续时间
  const formattedDuration = computed(() => {
    const totalSeconds = Math.floor(durationMs.value / 1000)
    const hours = Math.floor(totalSeconds / 3600)
    const minutes = Math.floor((totalSeconds % 3600) / 60)
    const seconds = totalSeconds % 60
    
    return {
      hours: hours.toString().padStart(2, '0'),
      minutes: minutes.toString().padStart(2, '0'),
      seconds: seconds.toString().padStart(2, '0'),
      totalMs: durationMs.value
    }
  })
  
  // 清理资源
  const cleanup = () => {
    if (worker) {
      worker.postMessage({ type: 'DESTROY' })
      worker.terminate()
      worker = null
    }
    
    if (cleanupListeners) {
      cleanupListeners()
      cleanupListeners = null
    }
  }
  
  onUnmounted(() => {
    cleanup()
  })
  
  return {
    formattedDuration,
    initDuration,
    cleanup,
    durationMs
  }
})
步骤3:创建使用Worker的持续时间组件

文件路径: src/components/DurationDisplay.worker.vue

vue 复制代码
<template>
  <div class="duration-display-worker">
    <div class="indicator" :class="{ 'worker-active': workerActive }"></div>
    <span class="time">
      {{ formattedDuration.hours }}:{{ formattedDuration.minutes }}:{{ formattedDuration.seconds }}
    </span>
    <el-tag v-if="workerActive" size="small" type="success">Worker</el-tag>
  </div>
</template>

<script setup>
import { onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { useTimeStoreWithWorker } from '@/stores/timeStore.worker.js'

const route = useRoute()
const timeStore = useTimeStoreWithWorker()

// 从路由参数获取初始时间
const initialDuration = route.params.initialDuration || 
                       route.query.initialDuration || 
                       0

// 是否使用Worker(可以根据浏览器支持动态决定)
const workerActive = typeof Worker !== 'undefined'

onMounted(() => {
  if (workerActive) {
    timeStore.initDuration(Number(initialDuration))
  } else {
    console.warn('当前浏览器不支持Web Worker')
    // 可以在这里切换到不使用Worker的版本
  }
})

onUnmounted(() => {
  timeStore.cleanup()
})

const formattedDuration = timeStore.formattedDuration
</script>

<style scoped>
.duration-display-worker {
  display: flex;
  align-items: center;
  gap: 10px;
  background: #2c3e50;
  color: white;
  padding: 10px 20px;
  border-radius: 8px;
  font-family: 'Roboto Mono', monospace;
}

.indicator {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #e74c3c;
}

.indicator.worker-active {
  background: #2ecc71;
  animation: pulse 2s infinite;
}

.time {
  font-size: 20px;
  font-weight: bold;
  letter-spacing: 1px;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}
</style>

第三部分:部署和优化建议

1. 路由配置(确保传递初始时间)
javascript 复制代码
// src/router/index.js
{
  path: '/your-page',
  name: 'YourPage',
  component: () => import('@/views/YourPage.vue'),
  props: (route) => ({
    // 传递初始持续时间参数
    initialDuration: route.query.initialDuration || 0
  })
}
2. 性能监控
javascript 复制代码
// src/utils/performanceMonitor.js
export const setupPerformanceMonitor = () => {
  // 监控内存使用
  if (performance.memory) {
    setInterval(() => {
      const memory = performance.memory
      const usedMB = Math.round(memory.usedJSHeapSize / 1024 / 1024)
      const totalMB = Math.round(memory.totalJSHeapSize / 1024 / 1024)
      
      if (usedMB > 100) { // 超过100MB
        console.warn('内存使用过高:', { usedMB, totalMB })
      }
    }, 30000)
  }
  
  // 监控时间偏差
  let lastCheckTime = Date.now()
  setInterval(() => {
    const now = Date.now()
    const elapsed = now - lastCheckTime
    lastCheckTime = now
    
    // 如果计时偏差超过2%,记录警告
    if (Math.abs(elapsed - 1000) > 20) {
      console.warn('计时偏差过大:', Math.abs(elapsed - 1000), 'ms')
    }
  }, 1000)
}
3. 生产环境配置
javascript 复制代码
// vite.config.js 或 webpack配置
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        // Worker文件单独处理
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
      }
    }
  },
  worker: {
    format: 'es',
    plugins: []
  }
})

第四部分:选择建议

选择方案A(不使用Worker)

  • 适用于大多数场景
  • 实现简单,维护成本低
  • 兼容性好
  • 推荐使用此方案

选择方案B(使用Worker)

  • 适用于对计时精度要求极高的场景
  • 页面有大量计算任务时,避免阻塞UI
  • 后台运行更稳定
  • 但实现复杂,兼容性需要考虑

最终建议实施步骤:

  1. 立即实施 :采用方案A(不使用Worker),代码简洁,解决主要问题
  2. 测试验证:在生产环境测试,监控时间同步情况
  3. 逐步优化 :如果仍有问题,再考虑方案B(使用Worker)
  4. 监控告警:添加时间偏差监控,超过阈值时告警

这样的分步实施方案既保证了快速解决问题,又为后续优化留下了空间。

相关推荐
随风一样自由7 小时前
React中实现iframe嵌套登录页面:跨域与状态同步解决方案探讨
javascript·react.js·ecmascript
m0_740043738 小时前
JavaScript
开发语言·javascript·ecmascript
爱学习的程序媛18 小时前
《JavaScript权威指南》核心知识点梳理
开发语言·前端·javascript·ecmascript
猫头虎-前端技术1 天前
小白也能做AI产品?我用 MateChat 给学生做了一个会“拍照解题 + 分步教学”的AI智能老师
前端·javascript·vue.js·前端框架·ecmascript·devui·matechat
幸运小圣1 天前
递归(Recursion)快速上手指南【JS例子】
开发语言·javascript·ecmascript
前端程序猿i1 天前
彻底搞懂防抖(Debounce)与节流(Throttle):源码实现与应用场景
开发语言·前端·javascript·vue.js·ecmascript
pandarking1 天前
[CTF]攻防世界:ics-05
开发语言·javascript·web安全·网络安全·ecmascript
怪我冷i2 天前
es6与es5的模块区别
前端·ecmascript·es6·ai写作
小离a_a3 天前
flex垂直布局,容器间距相等
开发语言·javascript·ecmascript