
在 Vue 3 中,虽然 props 本身是响应式的,但若在 setup() 中直接解构或静态计算 props 值(如字符串拼接、函数调用),会导致结果失去响应性;必须使用 computed 显式声明依赖关系,才能确保子组件随父组件数据更新而自动重渲染。
在 vue 3 中,虽然 props 本身是响应式的,但若在 `setup()` 中直接解构或静态计算 props 值(如字符串拼接、函数调用),会导致结果失去响应性;必须使用 `computed` 显式声明依赖关系,才能确保子组件随父组件数据更新而自动重渲染。
问题根源在于:你在 PersonLink 组件的 setup() 函数中,将 props.person.slug 和 NameFormatter.formatInverseName(props.person) 的结果直接赋值为普通变量(如 url 和 name),而非响应式引用。这些值仅在组件初始化时计算一次,后续 props.person 更新时不会重新求值——因为 JavaScript 原始值不具备响应式追踪能力。
✅ 正确做法是使用 computed 创建响应式计算属性。它会自动建立对 props.person 的依赖追踪,并在其任意响应式字段(如 slug、firstName、lastName)变化时触发重新计算。
以下是修复后的 PersonLink.vue 完整代码:
<template>
<router-link :to="url">{{ name }}</router-link>
</template>
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { NameFormatter } from 'src/common/formatters';
import { Person } from './person.models';
export default defineComponent({
props: {
person: {
type: Object as () => Person,
required: true,
},
},
setup(props) {
const url = computed(() => `/person/${props.person.slug}`);
const name = computed(() => NameFormatter.formatInverseName(props.person));
return {
url,
name,
};
},
});
</script>⚠️ 注意事项:
立即学习“前端免费学习笔记(深入)”;
- 不要解构 props(例如 const { person } = props),否则会丢失响应性连接。始终通过 props.xxx 访问以维持响应式代理链。
- 若 NameFormatter.formatInverseName 内部依赖 person 的响应式字段(如 person.firstName),则 computed 能正确捕获其变更;若该函数纯静态(如仅操作非响应式副本),仍需确保传入的是原始响应式对象。
- 确保 DetailedPerson[] 数据在父组件中通过 ref 或 reactive 声明(如 const people = ref<DetailedPerson[]>([])),否则 v-for 无法触发响应式更新。
? 总结:Vue 3 的响应式系统不会“自动提升”普通变量为响应式——你必须显式使用 ref、reactive 或 computed 来包裹依赖逻辑。computed 是处理派生状态(尤其是基于 props 的派生)的标准且高效方式,也是保持子组件与父组件数据同步的关键实践。


















