Vue3 SSR场景下如何保证watch内异步任务早于beforeUpdate及DOM更新执行
问题根本原因
- 测试代码存在逻辑缺陷
你写的testWatch没有将setTimeout包装为Promise,await testWatch()不会等待定时器执行,调用后会直接返回,相当于异步等待逻辑完全没有生效,这是测试结果失真的直接原因。 - Vue的watcher本身不会等待异步回调执行完成
就算修正了测试代码的Promise封装问题,还是会出现onBeforeUpdate先执行的情况:Vue的watch设计上就不会阻塞生命周期流程,不管回调是不是async函数,触发watch后只会同步执行到第一个await的位置,就会继续走后续的生命周期、DOM更新逻辑,不会等整个异步回调跑完。
兼容SSR的解决方案
方案1:使用路由守卫(最适配你的场景)
你需要监听路由参数变化、等待数据加载完成再更新组件,直接用vue-router提供的onBeforeRouteUpdate守卫即可,它本身支持异步逻辑,会等你逻辑执行完调用next()后再走组件更新流程,SSR端也能正常兼容:
import { useRoute, onBeforeRouteUpdate } from 'vue-router' setup() { const route = useRoute() // 封装数据加载逻辑 const loadProductData = async (productId) => { // 此处写你更新Vuex、处理业务的所有异步逻辑 await yourAsyncLogic(productId) } // 组件首次加载时执行 (async () => { await loadProductData(route.params.ProductID) })() // 路由参数变化时执行 onBeforeRouteUpdate(async (to, from, next) => { if (to.params.ProductID !== from.params.ProductID) { await loadProductData(to.params.ProductID) } next() }) onBeforeUpdate(() => { // 此处可以直接拿到加载完成的所有数据 }) }
方案2:增加渲染锁手动控制
如果需要保留watch的写法,可以加一个状态位控制组件渲染时机,避免数据未加载完成时渲染错误内容:
setup() { const isDataReady = ref(false) const route = useRoute() watch(() => route.params.ProductID, async (newId) => { isDataReady.value = false // 执行所有异步业务逻辑 await yourAsyncLogic(newId) isDataReady.value = true }, { deep: true, immediate: true }) onBeforeUpdate(() => { // 加判断避免数据未准备好时执行逻辑 if (!isDataReady.value) return // 此处写你的正常逻辑 }) }
对应模板最外层加判断即可:
<template> <div v-if="isDataReady"> <!-- 你的正常组件内容 --> </div> <div v-else> 加载中... </div> </template>
测试代码修正
如果你需要验证逻辑,把testWatch改成返回Promise的形式即可:
async function testWatch(){ return new Promise(resolve => { setTimeout(() => { console.log("I am watcher timeout"); resolve(); }, 3000); }) }
内容的提问来源于stack exchange,提问作者volume one
相关产品推荐
相关产品推荐

