Vue 3 onMounted 中控制同步与异步执行策略

Vue 3 onMounted 中控制同步与异步执行策略

在 Vue 3 的 onMounted 钩子中,控制多个函数的同步和异步执行有多种方法。以下是详细的技术方案:

📊 核心概念对比

执行方式 特点 适用场景
同步执行 顺序执行,前一个完成后才开始下一个 有依赖关系的操作
并行执行 同时启动,等待所有完成 独立的任务,提高效率
按条件执行 根据条件决定是否执行 条件加载、权限控制

一、同步执行控制

1. 基本同步顺序执行

javascript 复制代码
<script setup>
import { onMounted } from 'vue'

onMounted(() => {
  console.log('1. 开始同步执行')
  
  // 函数1
  const result1 = function1()
  console.log('函数1完成:', result1)
  
  // 函数2(依赖函数1的结果)
  const result2 = function2(result1)
  console.log('函数2完成:', result2)
  
  // 函数3(依赖函数2的结果)
  const result3 = function3(result2)
  console.log('函数3完成:', result3)
  
  console.log('所有同步函数执行完成')
})

function function1() {
  console.log('执行 function1')
  return 'function1 的结果'
}

function function2(input) {
  console.log('执行 function2,输入:', input)
  return input + ' -> 经过function2处理'
}

function function3(input) {
  console.log('执行 function3,输入:', input)
  return input + ' -> 经过function3处理'
}
</script>

输出

复制代码
1. 开始同步执行
执行 function1
函数1完成: function1 的结果
执行 function2,输入: function1 的结果
函数2完成: function1 的结果 -> 经过function2处理
执行 function3,输入: function1 的结果 -> 经过function2处理
函数3完成: function1 的结果 -> 经过function2处理 -> 经过function3处理
所有同步函数执行完成

2. 使用 async/await 控制异步函数同步执行

javascript 复制代码
<script setup>
import { onMounted } from 'vue'

onMounted(async () => {
  console.log('🚀 开始顺序执行异步任务')
  
  try {
    // 1. 等待第一个异步任务完成
    const user = await fetchUserData()
    console.log('✅ 用户数据获取完成:', user.name)
    
    // 2. 等待第二个异步任务(依赖第一个结果)
    const orders = await fetchUserOrders(user.id)
    console.log('✅ 订单数据获取完成,数量:', orders.length)
    
    // 3. 等待第三个异步任务
    const analytics = await initAnalytics(orders)
    console.log('✅ 分析工具初始化完成')
    
    console.log('🎉 所有任务顺序执行完成')
  } catch (error) {
    console.error('❌ 执行失败:', error)
  }
})

// 模拟异步函数
async function fetchUserData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ id: 1, name: '张三' })
    }, 1000)
  })
}

async function fetchUserOrders(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve([{ id: 1, product: '商品A' }, { id: 2, product: '商品B' }])
    }, 1500)
  })
}

async function initAnalytics(orders) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ events: orders.length })
    }, 500)
  })
}
</script>

执行时间:约 3 秒(1000 + 1500 + 500)


二、并行执行控制

1. 使用 Promise.all 并行执行

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

const loading = ref(true)
const data = ref({})

onMounted(async () => {
  console.log('🚀 开始并行执行任务')
  const startTime = Date.now()
  
  try {
    // 并行执行所有异步任务
    const [userData, configData, permissions] = await Promise.all([
      fetchUserData(),
      fetchConfigData(),
      fetchUserPermissions()
    ])
    
    const endTime = Date.now()
    console.log(`✅ 所有并行任务完成,耗时: ${endTime - startTime}ms`)
    
    // 整合数据
    data.value = { ...userData, ...configData, permissions }
    loading.value = false
    
    console.log('🎉 数据整合完成:', data.value)
  } catch (error) {
    console.error('❌ 并行执行失败:', error)
    loading.value = false
  }
})

async function fetchUserData() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('✅ 用户数据获取完成')
      resolve({ name: '张三', age: 25 })
    }, 2000)  // 模拟2秒延迟
  })
}

async function fetchConfigData() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('✅ 配置数据获取完成')
      resolve({ theme: 'dark', language: 'zh-CN' })
    }, 1500)  // 模拟1.5秒延迟
  })
}

async function fetchUserPermissions() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('✅ 权限数据获取完成')
      resolve(['read', 'write', 'delete'])
    }, 1000)  // 模拟1秒延迟
  })
}
</script>

输出

复制代码
🚀 开始并行执行任务
✅ 权限数据获取完成
✅ 配置数据获取完成
✅ 用户数据获取完成
✅ 所有并行任务完成,耗时: 2002ms
🎉 数据整合完成: {name: '张三', age: 25, theme: 'dark', language: 'zh-CN', permissions: ['read', 'write', 'delete']}

总耗时:约 2 秒(最慢的任务耗时)

2. 使用 Promise.allSettled 处理并行任务(不因失败中断)

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

const results = ref({})

onMounted(async () => {
  console.log('🚀 开始并行执行任务(包含容错)')
  
  // 使用 allSettled,即使某个任务失败也不会中断
  const settledResults = await Promise.allSettled([
    fetchUserData(),
    fetchConfigData(),
    fetchUserPermissions(),
    fetchOptionalData()  // 这个可能失败
  ])
  
  // 处理结果
  const successResults = {}
  const failedResults = {}
  
  settledResults.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      successResults[`task${index}`] = result.value
    } else {
      failedResults[`task${index}`] = result.reason
    }
  })
  
  results.value = { success: successResults, failed: failedResults }
  console.log('📊 执行结果:', results.value)
})

async function fetchUserData() {
  return Promise.resolve({ user: '张三' })
}

async function fetchConfigData() {
  return Promise.resolve({ theme: 'dark' })
}

async function fetchUserPermissions() {
  return Promise.resolve(['read', 'write'])
}

async function fetchOptionalData() {
  // 模拟可能失败的任务
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve({ optional: 'data' })
      } else {
        reject(new Error('Optional data fetch failed'))
      }
    }, 500)
  })
}
</script>

三、混合执行策略

1. 分组并行 + 顺序执行

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

const executionLog = ref([])

onMounted(async () => {
  console.log('🚀 开始混合执行策略')
  addLog('开始执行')
  
  try {
    // 阶段1:并行加载不依赖的数据
    addLog('阶段1: 并行加载基础数据')
    const [user, config] = await Promise.all([
      fetchUserData(),
      fetchConfigData()
    ])
    addLog('阶段1完成')
    
    // 阶段2:顺序执行依赖任务
    addLog('阶段2: 顺序加载依赖数据')
    const orders = await fetchUserOrders(user.id)
    addLog('订单数据加载完成')
    
    const analytics = await initAnalytics(orders)
    addLog('分析工具初始化完成')
    
    // 阶段3:并行执行最后步骤
    addLog('阶段3: 并行执行清理和通知')
    await Promise.all([
      sendAnalytics(analytics),
      updateUserLastLogin(user.id)
    ])
    addLog('阶段3完成')
    
    addLog('🎉 所有任务完成')
  } catch (error) {
    addLog(`❌ 执行失败: ${error.message}`)
  }
})

function addLog(message) {
  const timestamp = new Date().toLocaleTimeString()
  executionLog.value.push(`${timestamp}: ${message}`)
  console.log(message)
}

// 模拟函数
async function fetchUserData() {
  await delay(1000)
  return { id: 1, name: '张三' }
}

async function fetchConfigData() {
  await delay(800)
  return { theme: 'dark' }
}

async function fetchUserOrders() {
  await delay(1200)
  return [{ id: 1 }, { id: 2 }]
}

async function initAnalytics() {
  await delay(600)
  return { initialized: true }
}

async function sendAnalytics() {
  await delay(300)
}

async function updateUserLastLogin() {
  await delay(400)
}

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}
</script>

执行时间:约 3.6 秒(1000 + 1200 + 600 + 400)

2. 带并发限制的并行执行

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

const tasks = ref(Array.from({ length: 10 }, (_, i) => ({
  id: i + 1,
  status: 'pending',
  result: null
})))

onMounted(async () => {
  console.log('🚀 开始带并发限制的执行')
  
  // 并发数限制为3
  const concurrencyLimit = 3
  const taskPromises = tasks.value.map(task => task.id)
  
  // 分批次执行
  for (let i = 0; i < taskPromises.length; i += concurrencyLimit) {
    const batch = taskPromises.slice(i, i + concurrencyLimit)
    console.log(`执行批次 ${i / concurrencyLimit + 1}: 任务 ${batch.join(', ')}`)
    
    // 并行执行当前批次
    const batchPromises = batch.map(async taskId => {
      const task = tasks.value.find(t => t.id === taskId)
      task.status = 'running'
      
      try {
        const result = await simulateTask(taskId)
        task.status = 'completed'
        task.result = result
        console.log(`✅ 任务 ${taskId} 完成`)
      } catch (error) {
        task.status = 'failed'
        task.result = error.message
        console.log(`❌ 任务 ${taskId} 失败`)
      }
    })
    
    // 等待当前批次所有任务完成
    await Promise.all(batchPromises)
  }
  
  console.log('🎉 所有批次执行完成')
})

async function simulateTask(taskId) {
  // 模拟随机耗时任务
  const duration = 500 + Math.random() * 1000
  await delay(duration)
  
  // 模拟10%的失败率
  if (Math.random() < 0.1) {
    throw new Error(`任务 ${taskId} 模拟失败`)
  }
  
  return `任务 ${taskId} 结果,耗时 ${Math.round(duration)}ms`
}
</script>

四、高级控制模式

1. 使用队列控制执行顺序

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

class TaskQueue {
  constructor(concurrency = 1) {
    this.concurrency = concurrency
    this.running = 0
    this.queue = []
  }
  
  add(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({ task, resolve, reject })
      this.run()
    })
  }
  
  run() {
    while (this.running < this.concurrency && this.queue.length) {
      const { task, resolve, reject } = this.queue.shift()
      this.running++
      
      task()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.running--
          this.run()
        })
    }
  }
}

const executionOrder = ref([])

onMounted(async () => {
  console.log('🚀 使用队列控制执行顺序')
  
  // 创建并发数为2的队列
  const queue = new TaskQueue(2)
  
  // 添加任务到队列
  const tasks = [
    () => simulateTask('任务A', 1000),
    () => simulateTask('任务B', 800),
    () => simulateTask('任务C', 1200),
    () => simulateTask('任务D', 600),
    () => simulateTask('任务E', 900)
  ]
  
  // 记录执行顺序
  const addToOrder = (taskName) => {
    executionOrder.value.push({
      time: Date.now(),
      task: taskName
    })
  }
  
  // 并行执行所有任务(但限制并发数)
  const startTime = Date.now()
  addToOrder('开始执行')
  
  await Promise.all(tasks.map((task, index) => 
    queue.add(async () => {
      addToOrder(`开始 ${task.name}`)
      await task()
      addToOrder(`完成 ${task.name}`)
    })
  ))
  
  addToOrder(`全部完成,总耗时: ${Date.now() - startTime}ms`)
  console.log('📊 执行顺序:', executionOrder.value)
})

async function simulateTask(name, duration) {
  console.log(`开始执行 ${name}`)
  await delay(duration)
  console.log(`完成 ${name}`)
  return name
}
</script>

2. 使用 async/await 生成器控制流程

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

const steps = ref([])

onMounted(async () => {
  console.log('🚀 使用生成器控制执行流程')
  
  // 定义任务生成器
  async function* taskGenerator() {
    steps.value.push('1. 初始化')
    yield await initApp()
    
    steps.value.push('2. 加载用户数据')
    const user = yield await loadUserData()
    
    steps.value.push('3. 根据用户角色加载不同数据')
    if (user.role === 'admin') {
      yield await loadAdminData()
    } else {
      yield await loadUserSpecificData(user.id)
    }
    
    steps.value.push('4. 完成初始化')
    yield await finalizeInit()
  }
  
  // 执行生成器
  const generator = taskGenerator()
  let result = await generator.next()
  
  while (!result.done) {
    console.log('步骤完成:', result.value)
    result = await generator.next()
  }
  
  console.log('🎉 所有步骤完成')
  console.log('步骤记录:', steps.value)
})

async function initApp() {
  await delay(500)
  return '应用初始化完成'
}

async function loadUserData() {
  await delay(800)
  return { id: 1, name: '张三', role: 'admin' }
}

async function loadAdminData() {
  await delay(1000)
  return '管理员数据加载完成'
}

async function loadUserSpecificData() {
  await delay(600)
  return '用户数据加载完成'
}

async function finalizeInit() {
  await delay(300)
  return '初始化完成'
}
</script>

五、错误处理与重试机制

1. 带重试的异步执行

javascript 复制代码
<script setup>
import { onMounted, ref } from 'vue'

const attempts = ref({})

onMounted(async () => {
  console.log('🚀 开始带重试机制的执行')
  
  // 重试配置
  const retryConfig = {
    maxRetries: 3,
    retryDelay: 1000,
    onRetry: (taskName, attempt) => {
      console.log(`🔄 ${taskName} 第 ${attempt} 次重试`)
    }
  }
  
  // 执行带重试的任务
  const result = await executeWithRetry(
    fetchCriticalData,
    ['关键数据'],
    retryConfig
  )
  
  console.log('📊 最终结果:', result)
})

// 重试执行器
async function executeWithRetry(task, args, config) {
  const { maxRetries, retryDelay, onRetry } = config
  let lastError
  
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      if (attempt > 0 && onRetry) {
        onRetry(task.name, attempt)
      }
      
      attempts.value[task.name] = (attempts.value[task.name] || 0) + 1
      
      return await task(...args)
    } catch (error) {
      lastError = error
      console.log(`❌ ${task.name} 第 ${attempt} 次失败:`, error.message)
      
      if (attempt < maxRetries) {
        await delay(retryDelay)
      }
    }
  }
  
  throw lastError
}

// 模拟可能失败的任务
async function fetchCriticalData() {
  const shouldFail = Math.random() < 0.7  // 70%失败率
  await delay(300)
  
  if (shouldFail) {
    throw new Error('网络请求失败')
  }
  
  return { data: '重要数据' }
}
</script>

六、最佳实践总结

同步执行场景

javascript 复制代码
// 适用于有依赖关系的任务
onMounted(async () => {
  const data1 = await task1()      // 必须先完成
  const data2 = await task2(data1) // 依赖data1
  const data3 = await task3(data2) // 依赖data2
})

并行执行场景

javascript 复制代码
// 适用于独立任务,提高效率
onMounted(async () => {
  const [result1, result2, result3] = await Promise.all([
    task1(),  // 独立任务
    task2(),  // 独立任务
    task3()   // 独立任务
  ])
})

混合执行场景

javascript 复制代码
// 组合使用,先并行后顺序
onMounted(async () => {
  // 并行加载基础数据
  const [user, config] = await Promise.all([
    fetchUser(),
    fetchConfig()
  ])
  
  // 顺序处理依赖数据
  const orders = await fetchOrders(user.id)
  await processOrders(orders)
  
  // 最后并行清理
  await Promise.all([
    sendAnalytics(),
    updateCache()
  ])
})

错误处理最佳实践

javascript 复制代码
onMounted(async () => {
  try {
    // 主任务
    await mainTask()
  } catch (error) {
    console.error('主任务失败:', error)
    
    // 降级处理
    await fallbackTask()
  } finally {
    // 清理资源
    await cleanup()
  }
})

性能监控

javascript 复制代码
onMounted(async () => {
  const startTime = performance.now()
  
  // 执行任务
  await yourTasks()
  
  const endTime = performance.now()
  console.log(`⏱️ 执行耗时: ${Math.round(endTime - startTime)}ms`)
  
  // 可以发送到监控系统
  if (endTime - startTime > 2000) {
    console.warn('⚠️ 执行时间过长')
  }
})

🎯 核心要点

  1. 使用 await 控制顺序执行
  2. 使用 Promise.all() 控制并行执行
  3. 使用 Promise.allSettled() 处理可能失败的任务
  4. 合理组合顺序和并行执行优化性能
  5. 实现适当的错误处理和重试机制
  6. 使用队列控制并发数,避免资源耗尽

通过合理选择执行策略,可以显著优化组件初始化的性能和用户体验。

相关推荐
PascalMing1 小时前
告别 Nginx!ASP.NET Core 实现多域名 Vue 静态服务与代理转发
vue.js·nginx·asp.net
蜗牛攻城狮1 小时前
【Vue3实战】El-Table实现“超过3行省略,悬停显示全文”的完美方案(附性能优化)
前端·vue.js·性能优化·element-plus
孙12~1 小时前
前端vue3+vite,后端SpringBoot+MySQL
前端·html·学习方法
隔壁小邓1 小时前
vue的组件化的理解之单独拆分的组件&组件的封装
前端·javascript·vue.js
Ivanqhz2 小时前
图着色寄存器分配算法(Graph Coloring)
开发语言·javascript·python·算法·蓝桥杯·rust
困惑阿三2 小时前
全栈部署排雷手册:从 405 报错到飞书推送成功
服务器·前端·后端·nginx·阿里云·node.js·飞书
无名-CODING2 小时前
从零开始!Vue3+SpringBoot前后端分离项目Docker部署实战(下):Vue前端Nginx反代与致命坑点盘点
前端·spring boot·docker
我命由我123452 小时前
Element Plus 问题:选择框表单校验没有触发
开发语言·前端·javascript·html·ecmascript·html5·js
optimistic_chen2 小时前
【Vue3入门】vue-router 路由管理
前端·javascript·vue.js·路由·router