Vue3响应式
使用ref
- 可以使用 ref 创建一个响应式的数据:
<template>
<div>{{ name }}</div>
</template>
<script setup>
import { ref } from 'vue'
// 现在的 name 就是一个响应式数据
let name = ref('HuangBingQuan')
console.log(name)
console.log(name.value)
setTimeout(() => {
name.value = 'BingQuanHuang'
}, 2000)
</script>
<style lang="scss" scoped></style>
ref 返回的响应式数据是一个对象,我们需要通过 .value 访问到内部具体的值。模板中之所以不需要 .value,是因为在模板会对 ref 类型的响应式数据自动解包。
ref 可以持有任意的类型,可以是对象、数组、普通类型的值、Map、Set...
对象的例子:
<template>
<div>{{ Person.name }}</div>
<div>{{ Person.age }}</div>
</template>
<script setup>
import { ref } from 'vue'
// 现在的 Person 就是一个响应式数据
let Person = ref({
name: 'HuangBingQuan',
age: 18
})
setTimeout(() => {
Person.value.name = 'BingQuanHuang'
Person.value.age = 20
}, 2000)
</script>
<style lang="scss" scoped></style>
数组的例子
<template>
<div>{{ arr }}</div>
</template>
<script setup>
import { ref } from 'vue'
// 现在的 arr 就是一个响应式数据
let arr = ref([1, 2, 3])
setTimeout(() => {
arr.value.push(4, 5, 6)
}, 2000)
</script>
<style lang="scss" scoped></style>
第二个点,ref 所创建的响应式数据是具备深层响应式,这一点主要体现在值是对象,对象里面又有嵌套的对象:
<template>
<div>{{ Person.name }}</div>
<div>{{ Person.age }}</div>
<div>{{ Person.nested.count }}</div>
</template>
<script setup>
import { ref } from 'vue'
// 现在的 Person 就是一个响应式数据
let Person = ref({
name: 'Biil',
age: 18,
nested: {
count: 1
}
})
setTimeout(() => {
Person.value.name = 'Biil2'
Person.value.age = 20
Person.value.nested.count += 2
}, 2000)
</script>
<style lang="scss" scoped></style>
shallowRef API
- 如果想要放弃深层次的响应式,可以使用 shallowRef,通过 shallowRef 所创建的响应式,不会深层地递归将对象每一层转为响应式,而只会将 .value 的访问转为响应式:
const state = shallowRef({ count: 1});
// 这个操作不会触发响应式更新
state.value.count += 2;
// 只针对 .value 值的更改会触发响应式更新
state.value = { count: 2}
具体示例:
<template>
<div>{{ Person.name }}</div>
<div>{{ Person.age }}</div>
<div>{{ Person.nested.count }}</div>
</template>
<script setup>
import { shallowRef } from 'vue'
let Person = shallowRef({
name: 'HuangBingQuan',
age: 18,
nested: {
count: 1
}
})
// 下面的更新不会触发视图更新
setTimeout(() => {
Person.value.name = 'HuangBingQuan'
Person.value.age = 20
Person.value.nested.count += 2
}, 2000)
// 下面的更新会触发视图更新
setTimeout(() => {
Person.value = {
name: 'HuangBingQuan',
age: 30,
nested: {
count: 3
}
}
}, 4000)
</script>
<style lang="scss" scoped></style>
响应式数据的更新,带来了 DOM 的自动更新,但是这个 DOM 的更新并非是同步的,这意味着当响应式数据发生修改后,我们去获取 DOM 值,拿到的是之前的 DOM 数据:
<template>
<div id="container">{{ count }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
let count = ref(1)
let container = null
setTimeout(() => {
count.value = 2 // 修改响应式状态
console.log('第二次打印:', container.innerText)
}, 2000)
// 这是一个生命周期钩子方法
// 会在组件完成初始渲染并创建 DOM 节点后自动调用
onMounted(() => {
container = document.getElementById('container')
console.log('第一次打印:', container.innerText)
})
</script>
<style lang="scss" scoped></style>
如果想要获取最新的 DOM 数据,可以使用 nextTick,这是 Vue 提供的一个工具方法,会等待下一次的 DOM 更新,从而方便后面能够拿到最新的 DOM 数据。
<template>
<div id="container">{{ count }}</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
let count = ref(1)
let container = null
setTimeout(async () => {
count.value = 2 // 修改响应式状态
// 等待下一个 DOM 更新周期
await nextTick()
// 这个时候再打印就是最新的值了
console.log('第二次打印:', container.innerText)
}, 2000)
// 这是一个生命周期钩子方法
// 会在组件完成初始渲染并创建 DOM 节点后自动调用
onMounted(() => {
container = document.getElementById('container')
console.log('第一次打印:', container.innerText)
})
</script>
<style lang="scss" scoped></style>
如果不用 async await,那么就是通过回调的形式:
setTimeout(() => {
count.value = 2 // 修改响应式状态
// 等待下一个 DOM 更新周期
nextTick(() => {
// 这个时候再打印就是最新的值了
console.log('第二次打印:', container.innerText)
})
}, 2000)
使用 reactive
- reactive 通常将一个对象转为响应式对象
<template>
<div>{{ state.count1 }}</div>
<div>{{ state.nested.count2 }}</div>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({
count1: 0,
nested: {
count2: 0
}
})
setTimeout(()=>{
state.count1++
state.nested.count2 += 2;
},2000);
</script>
<style lang="scss" scoped></style>
Vue 中的响应式底层是通过 ProxyAPI 来实现的,但是这个 ProxyAPI 只能对对象进行拦截,无法对原始值进行拦截。
如果拦截原始值则包装为对象,然后再使用 reactive API
ref的背后其实也调用了reactive API
原始值:Object.defineProperty
复杂值:reactive API
shallowReactive API
- shallowReactive API,是浅层次的,不会深层次去转换成响应式。
<template>
<div>{{ state.count1 }}</div>
<div>{{ state.nested.count2 }}</div>
</template>
<script setup>
import { shallowReactive } from 'vue'
const state = shallowReactive({
count1: 0,
nested: {
count2: 0
}
})
setTimeout(()=>{
state.count1++
},2000);
setTimeout(()=>{
state.nested.count2++
},4000)
</script>
<style lang="scss" scoped></style>
使用细节
reactive局限性
- 使用 reactvie 创建响应式数据的时候,值的类型是有限的
- 只能是对象类型(object、array、map、set)
- 不能够是简单值(string、number、boolean)
- 不能够去替换响应式对象,否则会丢失响应式的追踪
let state = reactive({count : 0});
// 下面的这个操作会让上面的对象引用不再被追踪,从而导致上面对象的响应式丢失
state = reactive({count : 1})
- 对解构操作不友好,当对一个 reactvie 响应式对象进行解构的时候,也会丢失响应式
let state = reactive({count : 0});
// 当进行解构的时候,解构出来的是一个普通的值
let { count } = state;
count++; // 这里也就是单纯的值的改变,不会触发和响应式数据关联的操作
// 另外还有函数传参的时候
// 这里传递过去的也就是一个普通的值,没有响应式
func(state.count)
ref解包细节
- 所谓 ref 的解包,指的是自动访问 value,不需要再通过 .value 去获取值。例如模板中使用 ref 类型的数据,就会自动解包。
- ref作为reactvie对象属性
这种情况下也会自动解包
<template>
<div></div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const name = ref('HuangBingQuan')
const state = reactive({
name
})
console.log(state.name) // 这里会自动解包
console.log(name.value)
</script>
<style lang="scss" scoped></style>
本文作者:HuangBingQuan
本文链接:https://www.cnblogs.com/bingquan1/p/18393776
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步