提炼vue3中响应式数据(ref和reactive)、路由容易混淆的知识点
首先,重新对ref和reactive重新实例化再赋值给已定义的变量,是不会触发视图更新的。
因为视图setup时已经拿到了原本对象的引用,而不是重新定义的ref或reactive的引用。
调用方式和适用范围
ref
基本数据类型/对象,调用时使用obj.value.属性名,返回对象refimpl.
因此替换其value属性,如:
1 2 3
| const obj = ref({name: 'Tom'}) obj.value = {name: '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) console.log(myRef.value.bar)
|
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 })
console.log(obj.count === count.value)
|
将一个 ref 赋值给一个 reactive 属性时,也会被自动解包:
1 2 3 4 5
| const count = ref(1) const obj = reactive({}) obj.count = count
console.log(obj.count === count.value)
|
但遇到某个响应式数组或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})
|
路由传参
方法一: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>
|