You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不使用ref()时如何让孙组件渲染与父组件保持同步?

问题:孙组件依赖父组件onUpdated计算数据,渲染总是慢一步

我的孙组件依赖父组件onUpdated钩子中计算的data2,但孙组件的渲染总是比父组件慢一步。以下是简化后的代码:

App.vue

<script setup lang="ts">
import { ref } from 'vue'
import Child from './Child.vue'

const msg = ref(1)
</script>

<template>
<button @click="++msg">increase</button>
<h1>Me: {{ msg }}</h1>
  <Child :data = msg></Child>
</template>

Child.vue

<script setup lang="ts">
import { onUpdated } from 'vue'
import GrandChild from './grandchild.vue'

const props = defineProps<{ data : number }>()
let data2;

function complexFunction(data) {
  setTimeout(()=>data2 = props.data,200);
}

onUpdated(() => {
    complexFunction(props.data);
})

</script>

<template>
  <h2>Child: {{ data }}</h2>
  <GrandChild :data = data2 ></GrandChild>
</template> 

GrandChild.vue

<script setup lang="ts">

const props = defineProps<{ data : number }>()

</script>

<template>
  <h3>Grandchild: {{ data }}</h3>
</template>

实际场景说明

实际中complexFunction会用Leaflet.js这类库计算data2(是复杂对象而非简单数字),示例里用setTimeout模拟异步计算过程。

如果给data2用ref(),简化示例里孙组件能正常更新,但实际场景中complexFunction会导致onUpdated递归调用,引发栈溢出,所以想找不用ref()的同步更新方案。

现在有个部分解决方案,但只能每两次点击后正确更新孙组件。我需要实现的效果是:

Me: 9 Child: 9 Grandchild: 9

而不是:

Me: 9 Child: 9 Grandchild: 8


解决方案

方案1:手动触发组件更新(不用ref)

因为data2是非响应式变量,修改后需要手动通知Vue更新视图。可以在计算完成后,借助nextTick和组件实例触发更新:

修改Child.vue:

<script setup lang="ts">
import { onUpdated, nextTick, getCurrentInstance } from 'vue'
import GrandChild from './grandchild.vue'

const props = defineProps<{ data : number }>()
let data2;
const instance = getCurrentInstance()

function complexFunction(data) {
  setTimeout(()=>{
    data2 = props.data;
    // 手动触发组件重新渲染
    nextTick(() => {
      instance?.update()
    })
  },200);
}

onUpdated(() => {
    complexFunction(props.data);
})

</script>

<template>
  <h2>Child: {{ data }}</h2>
  <GrandChild :data = data2 ></GrandChild>
</template> 

原理:修改非响应式的data2后,手动调用组件的update方法,强制Vue重新渲染,孙组件就能拿到最新的data2值。

方案2:用watch替代onUpdated避免递归(兼容ref)

之前用ref()导致递归,是因为onUpdated会在组件每次更新(包括孙组件更新触发的父组件更新)时执行。换成watch精准监听props.data的变化,就能避免递归:

修改Child.vue:

<script setup lang="ts">
import { ref, watch } from 'vue'
import GrandChild from './grandchild.vue'

const props = defineProps<{ data : number }>()
const data2 = ref<number | undefined>()

function complexFunction(data) {
  setTimeout(()=>{
    data2.value = props.data;
  },200);
}

// 仅在props.data变化时执行计算
watch(() => props.data, (newVal) => {
  complexFunction(newVal)
})

</script>

<template>
  <h2>Child: {{ data }}</h2>
  <GrandChild :data = data2 ></GrandChild>
</template> 

原理:watch只会在props.data的实际值改变时触发,不会因为组件的其他更新重复执行,从根源避免了递归调用的问题。

方案3:用markRaw处理第三方库实例(针对复杂对象)

如果data2是Leaflet实例这类不需要响应式追踪的复杂对象,用markRaw包装后,既保留响应式更新能力,又不会触发不必要的响应式监听,避免递归:

<script setup lang="ts">
import { ref, watch, markRaw } from 'vue'
import GrandChild from './grandchild.vue'

const props = defineProps<{ data : number }>()
const data2 = ref<any>()

function complexFunction(data) {
  setTimeout(()=>{
    // 替换为实际的第三方库实例创建逻辑
    const libInstance = {};
    data2.value = markRaw(libInstance);
  },200);
}

watch(() => props.data, (newVal) => {
  complexFunction(newVal)
})

</script>

内容的提问来源于stack exchange,提问作者quilkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:47:14