ref:
使用
ref()
用来创建任意类型的响应式ref。
import {ref} from 'vue' let num = ref(0) // 创建一个number类型 num.value++ console.log(num.value)
ref()
会把传入的参数放入.value
的属性(property
)中,.value
是响应式的属性。
- 在
<script>
中访问num
的值时要访问.value
这个属性,直接访问num
是无效的。 - 在
<template>
中可以直接访问num
,而不能访问或修改.value
,因为自动“解包”。
当
ref
在模板中作为顶层property
被访问时,它们会被自动“解包”,所以不需要使用.value
。ref 在模板中的解包
<script setup>
<script setup>
import {ref} from 'vue'
let num = ref(0) // 创建一个number类型
num.value++ // 通过.value访问
console.log(num.value)
</script>
<template>
<!-- 直接访问 -->
<button @click="num++">
{{num}}
</button>
</template>
当 ref()
的参数是对象类型时
vue内部会使用reactive()自动转换它的.value
一个包含对象类型值的 ref 可以响应式地替换整个对象:
<script setup>
import {ref} from 'vue'
const objectRef = ref({ count: 0 })
// 这是响应式的替换
function replace(){
objectRef.value = { count: 20 }
}
console.log(objectRef.value.count)
</script>
<template>
<button @click="replace">
{{objectRef.count}}
</button> 响应式替换<br>
<button @click="objectRef.count++">
{{objectRef.count}}
</button>
改变它的值也是响应式的
</template>
ref
被传递给函数 或 从一般对象上被解构时,不会丢失响应性:
const obj = {
foo: ref(1),
bar: ref(2)
}
// 该函数接收一个 ref,需要通过 .value 取值
// 但它会保持响应性
callSomeFunction(obj.foo)
// 仍然是响应式的
const { foo, bar } = obj
一言以蔽之,ref()
使我们能创造一种任意值的 “引用” 并能够不丢失响应性地随意传递。这个功能非常重要,因为它经常用于将逻辑提取到 组合式函数 中。
ref解包
上面的代码中有的地方用.value
,有的地方不用.value
,这里面是有区分的,不可用错。
ref模板解包
上面已经有所体现:ref使用
所以我们给出以下 object:
const object = { foo: ref(1) }
下面的表达式将不会像预期的那样工作:
{{ object.foo + 1 }} |
渲染的结果会是一个 [object Object]
,因为 object.foo
是一个 ref
对象。我们可以通过让 foo
成为顶级property
来解决这个问题:
const { foo } = object
{{ foo + 1 }}
现在渲染结果将是 2。
需要注意的是,如果一个 ref 是文本插值(即一个 {{ }} 符号)计算的最终值,它也将被解包。>因此下面的渲染结果将为 1:
{{ object.foo }}
这只是文本插值的一个方便功能,相当于 {{ object.foo.value }}
。
ref在响应式对象中的解包
当一个 ref 作为一个响应式对象的 property 被访问或更改时,它会自动解包,因此会表现得和一般的 property 一样:
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
如果将一个新的 ref 赋值给一个关联了已有 ref 的 property,那么它会替换掉旧的 ref:
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1
只有当嵌套在一个深层响应式对象内时,才会发生 ref 解包。当其作为浅层响应式对象的 property 被访问时不会解包。
数组和集合类型的 ref 解包
不像响应式对象,当 ref 作为响应式数组或像 Map 这种原生集合类型的元素被访问时,不会进行解包。
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)
reactive()
使用reactive()
绑定一个响应式对象。
<script setup>
// vue3特点,需要先导入要使用的api
import {reactive} from 'vue'
const state = reactive({count:1})
function add(){
state.count++
}
</script>
<template>
<div>
{{state.count}}
</div>
<button @click="add">{{state.count}}</button>
</template>
深层响应性
在vue中,默认是深层响应性的,在上面的例子当中,如果count不是一个数字,而是一个对象,这个对象的成员也是具有响应性的。
<script setup>
// vue3特点,需要先导入要使用的api
import {reactive} from 'vue'
const state = reactive({
count:{num:1}
})
function add(){
state.count.num++
}
</script>
<template>
<div>
{{state.count.num}}
</div>
<button @click="add">{{state.count.num}}</button>
</template>
reactive() 的局限性
reactive()
只能用于对象,数组,Map,Set这样的集合类型,不能用于数字,字符串,布尔等原始类型- 只有
reactive()
包裹的那个对象具有响应性
Vue 的响应式系统是通过
property
访问进行追踪的,因此必须始终保持对该响应式对象的相同引用。不可以随意地“替换”一个响应式对象,这将导致对初始引用的响应性连接丢失:
let state = reactive({ count: 0 })
// 上面的引用 ({ count: 0 }) 将不再被追踪(响应性连接已丢失!)
state = reactive({ count: 1 })
同时这也意味着当我们将响应式对象的 property
赋值或解构至本地变量时,或是将该 property
传入一个函数时,我们会失去响应性:
const state = reactive({ count: 0 })
// n 是一个局部变量,同 state.count
// 失去响应性连接
let n = state.count
// 不影响原始的 state
n++
// count 也和 state.count 失去了响应性连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收一个普通数字,并且
// 将无法跟踪 state.count 的变化
callSomeFunction(state.count)