Vue的 响应性语法糖 文档中 响应式 props
解构 部分提到了:
和.value
类似,为了保持响应性,你始终需要以props.x
的方式访问这些prop
。这意味着你不能够解构defineProps
的返回值,因为得到的变量将不是响应式的、也不会更新。
真的是这样吗?解构 defineProps
的返回值得到的变量将不是响应式,也不会更新。
例子1 父组件响应式数据改变 => 子组件解构出来的 proxy
对象
- 父组件每隔一段时间就更新响应式数据,在子组件的
onUpdated
钩子中总是可以看到更新后的数据 - 就算我们给响应式数据的value重新赋值一个值,在子组件的
onUpated
钩子中也总是可以看到更新后的数据
// Parent.vue
<script setup>
import Child from "./Child.vue"
import { onMounted, ref } from "vue";
const person = ref([
{ name: "peter", age: 18 },
{ name: "tom", age: 20 }
])
let cnt = 0;
setInterval(() => {
person.value[0].name += cnt;
++cnt;
}, 2000)
function update() {
person.value = []
}
</script>
<template>
<Child :person="person" />
<button @click="update()">update person.value</button>
</template>
//Child.vue
<script setup>
import { onMounted, onUpdated } from "vue";
const { person } = defineProps(["person"]);
onMounted(() => {
console.log("mounted person: ", JSON.stringify(person));
})
onUpdated(() => {
console.log("updated person: ", JSON.stringify(person));
})
</script>
<template>
<div v-for="item in person" class="flex gap-2 items-center">
<span>{{item.name}}</span>
<span>{{item.age}}</span>
</div>
</template>
<style scoped>
.flex {
display: flex;
}
.gap-2 {
gap: 10px;
}
.items-center {
align-items: center;
}
</style>
例子2:改变子组件解构出来的 proxy
=> 父组件的响应式数据
- 子组件每隔一段时间就更新响应式数据,在父组件的
onUpdated
钩子中总是可以看到更新后的数据。在视图上我们也可以看到父组件上的响应式数据和子组件上解构出来的proxy
在一起变化。
//Parent.vue
<script setup>
import Child from "./Child.vue"
import { onMounted, onUpdated, ref } from "vue";
const person = ref([
{ name: "peter", age: 18 },
{ name: "tom", age: 20 }
])
onMounted(() => {
console.log("mounted person: ", JSON.stringify(person.value));
})
onUpdated(() => {
console.log("updated person: ", JSON.stringify(person.value));
})
</script>
<template>
<div v-for="item in person" class="flex gap-2 items-center">
<span>{{item.name}}</span>
<span>{{item.age}}</span>
</div>
<div>*****************************************************</div>
<Child :person="person" />
</template>
<style scoped>
.flex {
display: flex;
}
.gap-2 {
gap: 10px;
}
.items-center {
align-items: center;
}
</style>
//Child.vue
<script setup>
import { onMounted, onUpdated } from "vue";
const { person } = defineProps(["person"]);
let cnt = 0;
setInterval(() => {
person[0].name += cnt;
++cnt;
}, 2000)
function update() {
}
</script>
<template>
<div v-for="item in person" class="flex gap-2 items-center">
<span>{{item.name}}</span>
<span>{{item.age}}</span>
</div>
<button @click="update()">update person.value</button>
</template>
<style scoped>
.flex {
display: flex;
}
.gap-2 {
gap: 10px;
}
.items-center {
align-items: center;
}
</style>
所谓“丢失响应性”,它指的是这种情况:👉传送门👈。
注意组件一和组件二在解构写法上的区别。
这里涉及的就是 @陟上晴明 提到的 Props Destructure Transform 这一特性:
这种写法,也和原题中的代码写法是一样的,即直接对
defineProps
的返回值解构。这种情况下父组件改变传入的 props,子组件也能获得最新的值。但你依然不能这么写:
注意这里不是直接解构
defineProps
的返回值,而是先用一个中间变量存储、然后再解构这个中间变量。这种情况下父组件再改变 props,子组件就不会得到最新的值了。因为 Props Destructure Transform 这个特性是发生在编译阶段的,Vue 会捕获 script setup 里对defineProps
的解构;而后面这种写法的解构实际发生在运行阶段,Vue 捕获不到。而所谓“丢失响应式”,针对的也是后一种这种写法。(当然了,没这个特性之前,前一种写法也一样丢失)
P.S. 多补充一点,为啥文档里要强调 props 的解构会失去响应式,是因为除了 script setup 以外,还有一种最常见的错误写法是这样的:
为啥是错误写法应该不难理解,就不解释了。