为什么 reactive 创建的对象解构会失去响应式?
例如:
export default {
setup() {
const obj = reactive({
foo: 1,
bar: 2,
})
return {
...obj
}
}
}
<template>
<p>{{ foo }} / {{ bar }}</p>
</template>
foo bar 不具有响应式,也就是响应丢失。当我们修改响应式数据时,例如 obj.foo,不会触发重新渲染。
首先要深刻理解 Proxy。reactive 创建了普通对象的 Proxy 对象,也就是代理对象:
// 普通对象
const plainObj = {
a: 1,
b: 2,
}
// 代理对象
const proxyObj = new Proxy(plainObj, {
get(target, key) {
console.log('get: ', key);
return target[key]
},
set(target, key, value) {
console.log('set: ', key, value);
target[key] = value
}
})
代理对象和普通对象的区别是,它拦截了对象的操作。它提供了另外一种操作对象的方式,也就是增加了拦截功能。
好像有点废话,plainObj != proxyObj,但是因为对象是引用的存在,所以对代理对象的任何操作也就是对它所代理的普通对象的操作。代理对象也能解构,就像解构它所代理的普通对象一样,这也很好理解,解构就像读取对象的属性赋值给变量,代理对象理所当然的可以读取属性。
实际上,我们可以把 plainObj 和 proxyObj 看做是同一个对象,但是因为 proxyObj 有一种特殊的能力,我们对 proxyObj 的所有操作都具有特殊的能力。而一旦解构了 proxyObj,生成了一些变量,那些变量自然不具有那些特殊的能力(有特殊能力的只是 proxyObj 本身)。
ref unwrapping ?
ref 在 setup 中需要写 .value 获取值,为什么在 template 又自动 unwrapping?
然而 unwrapping 只发生在 ref 变量为 template 的顶层属性时,为什么?
又,当一个 ref 是文本插值的最终求值时,又会 unwrapping。
也就是:
const object = { foo: ref(1) }
// 这样是不可以的
{{ object.foo + 1 }}
// 这样是可以的
{{ object.foo }}
为什么这么混乱呢?
以及,Ref Unwrapping in Reactive Objects 和 Ref Unwrapping in Arrays and Collections,又不一样。ref 作为 reactive 的属性,会自动 unwrapping,而 ref 在数组中和 Set、Map 中又不会。
computed set 的意义?
computed Best Practices 说:
Avoid mutating computed value#
The returned value from a computed property is derived state. Think of it as a temporary snapshot - every time the source state changes, a new snapshot is created. It does not make sense to mutate a snapshot, so a computed return value should be treated as read-only and never be mutated - instead, update the source state it depends on to trigger new computations.
Vue 官方文档说不建议修改 computed 值,又提供 set 的方式创建可修改的 computed,到底是什么意思?
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
这和 computed 的原意也是违背的,computed 是根据一个值计算另外一个值,也就是 A --> B,现在通过 set 可以做到 B--> A。从单向关系变为双向关系。
如何 watch props ?
Vue2 监听 props 时:
watch: {
xxx: { // props 属性名
}
}
直接对 props 属性进行监听,里面进行配置。
Vue3 很容易写成:
watch(props.xxx,
(newVal, oldVal) => {
// console.log(newVal, oldVal)
}
)
这种写法不生效,看文档,watch source types 可以是 ref、reactive 等,但是 props 不是 reactive 对象吗?打印 props 确实是 Proxy 类型,问题是当 reactive 对象解构或者作为函数参数传递的时候,就失去了响应式连接。参考:vuejs.org/guide/essen…
所以 props.xxx 作为 watch 参数的时候,就是一个 plain object,如何解决呢?watch source types 还有一种是 a getter function,所以这样写就可以了:
watch(
() => props.xxx,
(newVal, oldVal) => {
// console.log(newVal, oldVal)
}
)
当然还有其他的解决方法,例如将 props.xxx 转为 ref。