vue3中响应式数据(ref和reactive)、路由
提炼vue3中响应式数据(ref和reactive)、路由容易混淆的知识点
ref和reactive
首先,重新对ref和reactive重新实例化再赋值给已定义的变量,是不会触发视图更新的。
因为视图setup时已经拿到了原本对象的引用,而不是重新定义的ref或reactive的引用。
调用方式和适用范围
ref
基本数据类型/对象,调用时使用obj.value.属性名
,返回对象refimpl.
因此替换其value属性,如:1
2
3const obj = ref({name: 'Tom'})
obj.value = {name: 'Jerry'}
// 此时模板中对obj.value.name的引用还是响应式的,即从Tom变为Jerry
如果对象中包含了嵌套的ref,它们将被深层地解包.1
2
3
4
5
6
7
8
9
10const count = ref(0)
const obj = {
foo: ref(1),
bar: 'hello'
}
const myRef = ref(obj)
console.log(myRef.value.foo) // 1
console.log(myRef.value.bar) // 'hello'
// 不需要 .value 来访问 foo,回归了基本数据类型的访问方式
ref和v-bind
ref获取DOM节点并修改样式以及通过v-bind:style进行动态绑定样式
在Vue中,通过ref组件对style进行动态绑定之后
要修改样式必须重新赋值,而不能直接通过squareRef.value.style.样式名
修改样式,并且修改的结果会完全清除并覆盖之前的样式。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<template>
<div :style="squareStyle">
</div>
</template>
<script setup>
import {onMounted, ref} from 'vue'
const squareStyle = ref()
onMounted(() => {
squareStyle.value = {
width: '200px',
height: '200px',
backgroundColor: 'skyblue',
border: '5px dotted blue',
borderRadius: '110px'
}
squareStyle.value = {
width: '300px',
height: '300px',
backgroundColor: 'greenyellow',
border: '5px dashed green'
}
})
</script>
reactive
reactive:对象,调用时直接使用obj.属性名
,返回对象proxy.
要整体改变对象,需要使用Object.assign
来更新多个属性
同样,初始化时遇到对象中的ref字段,也会被深层解包。1
2
3
4
5const count = ref(1)
const obj = reactive({ count })
// ref 会被解包
console.log(obj.count === count.value) // true
将一个 ref 赋值给一个 reactive 属性时,也会被自动解包:1
2
3
4
5const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count === count.value) // true
但遇到某个响应式数组或Map这样原生集合类型中的 ref时,不会执行解包1
2
3
4
5const books = reactive([ref('Vue 3 Guide')])
console.log(books[0].value)
const map = reactive(new Map([['count', ref(0)]]))
console.log(map.get('count').value)
watch的注意事项
在用watch时,reactive已经自动开启隐式的深度监听,而ref则需要手动开启深度监听。
但注意:在深层级模式时,如果回调函数由于深层级的变更而被触发,那么新值和旧值将是同一个对象(地址没变)。1
2
3watch(() => ref.value, (newVal, oldVal) => {
console.log('ref value changed')
}, {deep: true})
若需要监视ref/reactive对象的某一基本类型属性(因为监视的值必须是一个对象(的地址)。):
- 监听的getter函数的返回值。
- reactive用
toRefs
提出来
对象类型也建议提出来或用getter函数(这就是watch的三种情况)
当值整个改变时,由于地址发生改变,因此即使开启深度监视,也不会触发回调函数。
通俗来说,应该根据监视的值是对象本身 对象的地址还是对象内部 基本类型的属性来决定。1
2
3
4
5
6
7const test= reactive({a: 1, b: 2,c:{d:3,e:4}})
watch(() => test.c, (newVal, oldVal) => {
console.log('test.c changed')
}, {deep: true})
// d e会触发回调
// 但若直接改变test.c的值,如test.c = {d: 5, e: 6},则不会触发回调
// 因此时的test.c是一个新的对象,地址发生了改变
路由传参
方法一:query 及响应式对象的解构赋值
1 | <!--路由组件...--> |
注意:当b是pinia的store时,toRefs解构出来的对象含有store的所有属性和方法,这个时候应该用storeToRefs
来解构。
方法二:params
1 | <!--需要在路由中配置/:params--> |
方法三:进一步借助路由配置的props
1 | <script> |