Loading

vue3生态 学习笔记-响应式


vue2和vue3中,选项式api的写法中data里面声明的变量都是响应式的,无需绑定。组合式api的写法中,需要手动指定响应式。

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() 的局限性

  1. reactive()只能用于对象,数组,Map,Set这样的集合类型,不能用于数字,字符串,布尔等原始类型
  2. 只有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)

用 ref() 解决 reactive() 的局限性

使用

ref()用来创建任意类型的响应式ref。

import {ref} from 'vue'
let num = ref(0) // 创建一个number类型
num.value++
console.log(num.value)

ref()会把传入的参数放入.value的属性(property)中,.value是响应式的属性。

  1. <script>中访问num的值时要访问.value这个属性,直接访问num是无效的。
  2. <template>中可以直接访问num,而不能访问或修改.value,因为自动“解包”。

ref 在模板中作为顶层 property 被访问时,它们会被自动“解包”,所以不需要使用 .valueref 在模板中的解包

其他类型解包

<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)
posted @ 2022-07-25 15:05  sq800  阅读(166)  评论(0编辑  收藏  举报