vue3 响应式 Ref和Reactive 中的一些细节

vue3 响应式API Ref和Reactive 中的一些细节

shallowRef(浅响应式)

const state= shallowRef({count:1});
state.value.count+=2; // 不会触发页面的响应式更新
state.value={count:2} // 才会触发更新

数据更新

响应式数据的更新*(响应式数据更新是同步的),带来了DOM的自动更新,但是DOM的更新不是同步的*,这意味这当响应式数据发生改变之后,我们去获取DOM值,拿到的还是之前的DOM值

如果要获取更新过的DOM数据要使用nextTick

shallowReactive(第一层的数据改变会触发更新)

和上面shallowRef作用一样

const state= shallowReactive({count1:1,nested:{count2:2}});
state.count1+=2; // 会触发页面的更新
state.nested.count2+=2 // 不会触发更新

reactive的局限性

当对reactive对象进行解构时会丢失响应式

在部分函数传参中会丢失响应式

let testReactive = reactive({
	count: 1,
});
onMounted(() => {
	setTimeout(() => {
		func(testReactive); // 传递一整个reactive对象响应式不会丢失
        func(testReactive.count); // 传递reactive对象的某个值响应式丢失
	}, 1000);
});

const func = (state) => {
	state.count ? (state.count = 4) : (state = 2);
};

ref解包细节

const name1 = ref(1);
const name2 = reactive({ name1 });
console.log("n1", name1.value); // 1
console.log("n2", name2); // 1 自动解包 (解构出name1再加.value)

如果时shallowReactive就不会自动解包

  1. 为什么 Bill 渲染出来有双引号?
  2. 为什么 2 秒后界面没有渲染 Smith ?
<template>
  <div>{{ obj.name }}</div>
</template>

<script setup>
import { ref, shallowReactive } from 'vue'
const name = ref('Bill') // name 是一个 ref 值
const obj = shallowReactive({
  name
})
setTimeout(() => {
  obj.name = 'John'
}, 1000)
setTimeout(() => {
  name.value = 'Smith'
}, 2000)
</script>

<style lang="scss" scoped></style>

答案:

  1. 因为使用的是 shallowReactive,shallowReactive 内部的 ref 是不会自动解包的
  2. 1秒后,obj.name 被赋予了 John 这个字符串值,这就使得和原来的 ref 数据失去了联系

如果想要渲染出 Smith,修改如下:

import { ref, shallowReactive } from 'vue'
const name = ref('Bill') // name 是一个 ref 值
const obj = shallowReactive({
  name
})
setTimeout(() => {
  name.value = 'John'
}, 1000)
setTimeout(() => {
  name.value = 'Smith'
}, 2000)
  1. 数组和集合里面使用 ref

如果将 ref 数据作为 reactvie 数组或者集合的一个元素,此时是不会自动解包的

// 下面这些是官方所给的例子
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)
<template>
  <div></div>
</template>

<script setup>
import { ref, reactive } from 'vue'
const name = ref('Bill')
const score = ref(100)
const state = reactive({
  name,
  scores: [score]
})
console.log(state.name) // 会自动解包
console.log(state.scores[0]) // 不会自动解包
console.log(state.scores[0].value) // 100
</script>

<style lang="scss" scoped></style>
  1. 在模板中的自动解包

在模板里面,只有顶级的 ref 才会自动解包。

<template>
  <div>
    <div>{{ count }}</div>
    <div>{{ object.id }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0) // 顶级的 Ref 自动解包
const object = {
  id: ref(1) // 这就是一个非顶级 Ref 不会自动解包
}
</script>

<style lang="scss" scoped></style>

上面的例子,感觉非顶级的 Ref 好像也能够正常渲染出来,仿佛也是自动解包了的。

但是实际情况并非如此。

<template>
  <div>
    <div>{{ count + 1 }}</div>
    <div>{{ object.id + 1 }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0) // 顶级的 Ref 自动解包
const object = {
  id: ref(1) // 这就是一个非顶级 Ref 不会自动解包
}
</script>

<style lang="scss" scoped></style>

例如我们在模板中各自加 1 就会发现上面因为已经解包出来了,所以能够正常的进行表达式的计算。

但是下面因为没有解包,意味着 object.id 仍然是一个对象,因此最终计算的结果为 [object Object]1

因此要访问 object.id 的值,没有自动解包我们就手动访问一下 value

<template>
  <div>
    <div>{{ count + 1 }}</div>
    <div>{{ object.id.value + 1 }}</div>
  </div>
</template>

-EOF-

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值