ref和reactive
区别
- ref可以定义基本数据类型和对象数据类型;reactive可以定义对象数据类型
- ref创建的对象必须使用
.value
- reactive重新分配一个新对象,会失去响应式(可以使用
Object.assign
去整体替换)
使用原则:
- 若需要一个基本类型的响应式数据,必须使用
ref
- 若需要一个响应式对象,层级不深,
ref
和reactive
都可以 - 若需要一个响应式对象,层级较深,推荐使用
reactive
原理
reactive
Proxy对象,简单描述:
1 |
|
解释问题:
为什么对reactive响应式对象重新赋值会丢失响应式?
1
2let test = reactive({ a: 1, b: 2 })
test = { a:3, b:4 } // 会丢失响应式这是因为test实际上是个Proxy对象,重新赋值变成了一个普通对象。
那为什么重新赋值一个reactive对象也会丢失响应式呢?
1
2let test = reactive({ a: 1, b: 2 })
test = reactive({ a:3, b:4 }) // 丢失响应这是因为上面的代码只是一个简单描述,实际上reactive函数中做的事情还有很多,重新赋值一个reactive对象,虽然实现了数据上的代理,但是丢失了与视图之间的依赖关系,所以视图无法更新,也就是所谓的失去了响应式。
为什么reactive解构赋值会丢失响应式?
准确来说,只有普通类型的属性会丢失响应,如果是对象类型的属性,响应式仍有效。
1
2
3
4
5
6
7
8
9
10let test = reactive({
a: 1,
b: 2,
c: {
x: 1,
y: 2,
},
})
const { a, b, c } = test
console.log(a, b, c) // 1 2 Proxy(Object)这是因为解构赋值等价于:
1
2
3const a = test.a
const b = test.b
const c = test.c由于c是对象类型,在get函数中被再次包装,所以不会丢失响应式。
为什么reactive响应式对象中的属性赋予一个新对象,该新对象具有响应式?
1
2
3
4
5
6let test = reactive({
a: 1,
b: 2
})
test.c = { x: 1, y: 2 }
console.log(test.a, test.b, test.c) // 1 2 Proxy(Object)原因和上一个问题其实是一样的,因为触发了get方法,在get方法中进行了再次包装
reactive响应式对象中的属性使用ref包裹,为什么不需要再使用.value?
1
2
3
4
5let test = reactive({
a: 1,
b: ref(2),
})
console.log(test.a, test.b) // 1 2,b可以直接访问到,而不需要test.b.value原因是get方法里对ref对象自动解包
ref
为什么会出现ref响应式对象?因为Proxy只能用来包装对象,无法包装基础类型数据,所以需要自己实现一个包装类:
1 |
|
解释问题:
为什么ref需要用
.value
访问其中值?因为ref是自己包装的一个类,为了使基础数据类型也具有响应式,所以需要
.value
访问其中值。为什么ref包裹对象,其value是个Proxy?
ref的底层实现中,如果是对象类型,实际上仍然依靠reactive方法。
为什么对reactive响应式对象重新赋值会丢失响应式,而对ref响应式对象.value重新赋值不会?
1
2
3
4
5
6let test = ref({
a: 1,
b: 2,
})
test.value = { a: 4, b: 5 }
console.log(test) // RefImpl,其中的value仍然是Proxy对reactive响应式对象重新赋值会丢失响应式已经在上文中解释过了,
而对ref响应式对象.value重新赋值,实际上触发了类中的set方法,如果是对象,再次经过reactive包装,所以依然是个响应式对象。
ref响应式对象中的属性使用ref包裹,为什么不需要再使用.value?
1
2
3
4
5let test = ref({
a: 1,
b: ref(2),
})
console.log(test.value.a, test.value.b) // 1 2,b可以直接访问到,而不需要test.value.b.value原因其实和上文中说到的一样,由于ref包裹的是对象,所以test.value实际是靠reactive包装的。
reactive中的get方法会对ref对象进行自动解包。