vue3中响应式数据(ref和reactive)、路由

提炼vue3中响应式数据(ref和reactive)、路由容易混淆的知识点

ref和reactive

首先,重新对ref和reactive重新实例化再赋值给已定义的变量,是不会触发视图更新的。

因为视图setup时已经拿到了原本对象的引用,而不是重新定义的ref或reactive的引用。

调用方式和适用范围

ref

基本数据类型/对象,调用时使用obj.value.属性名,返回对象refimpl.

因此替换其value属性,如:

1
2
3
const obj = ref({name: 'Tom'})
obj.value = {name: 'Jerry'}
// 此时模板中对obj.value.name的引用还是响应式的,即从Tom变为Jerry

如果对象中包含了嵌套的ref,它们将被深层地解包.

1
2
3
4
5
6
7
8
9
10
const 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
5
const count = ref(1)
const obj = reactive({ count })

// ref 会被解包
console.log(obj.count === count.value) // true

将一个 ref 赋值给一个 reactive 属性时,也会被自动解包:

1
2
3
4
5
const count = ref(1)
const obj = reactive({})
obj.count = count

console.log(obj.count === count.value) // true

但遇到某个响应式数组或Map这样原生集合类型中的 ref时,不会执行解包

1
2
3
4
5
const 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
3
watch(() => ref.value, (newVal, oldVal) => {
console.log('ref value changed')
}, {deep: true})
  • 若需要监视ref/reactive对象的某一基本类型属性(因为监视的值必须是一个对象(的地址)。):

    • 监听的getter函数的返回值。
    • reactive用toRefs提出来
  • 对象类型也建议提出来或用getter函数(这就是watch的三种情况)
    当值整个改变时,由于地址发生改变,因此即使开启深度监视,也不会触发回调函数。
    通俗来说,应该根据监视的值是对象本身 对象的地址还是对象内部 基本类型的属性来决定。

    1
    2
    3
    4
    5
    6
    7
    const 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!--路由组件...-->
<template>
<!--... -->
<!-- to有字符串写法to='/detail'和对象写法 -->
<router-link :to="{
path: '/detail',
query: {id: data.id}}">
</router-link>
<!--...-->
</template>

<script setup>
import {reactive} from 'vue'
const data = reactive({id: 1}) // 传递的数据可以为响应式对象
</script>

<!--详情页组件...-->
<template>
<div>{{id}}</div>
</template>

<script setup>
import {reactive,toRefs} from 'vue'
const route = useRoute()
// 直接拿到的变量是非响应式变量,应该使用`toRefs`或者`reactive`将其转换为响应式变量。
const id = reactive(route.query.id)//不解构
const {id} = toRefs(route.query)//解构

</script>

注意:当b是pinia的store时,toRefs解构出来的对象含有store的所有属性和方法,这个时候应该用storeToRefs来解构。

方法二:params

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--需要在路由中配置/:params-->
routes: [
{name: 'detail', path: '/detail/:id', component: Detail}}
]
<!--其他不变-->
<template>
<router-link :to="{
name: 'detail',
params: {id: data.id}}">
</router-link>
</template>

<!--详情页组件...-->

<script setup>
const {id} = toRefs(route.params)
</script>

方法三:进一步借助路由配置的props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
// 一、设置props为true,这样必须通过params传递
routes: [
{name: 'detail', path: '/detail/:id', component: Detail, props: true}}
]
// 二、设置props为对象(写死,用的少)
routes: [
{name: 'detail', path: '/detail/:id', component: Detail, props: {id: 1}}
]
// 三、设置props为函数(动态)
routes: [
{name: 'detail', path: '/detail/:id', component: Detail, props: route => ({id: route.query})}//或者route.params(但不如一简洁)
]

</script>