Vue3响应式基础

声明响应式状态

ref()

在组合式API中,推荐使用ref()函数来声明响应式状态:
import { ref } from ‘vue’
const count = ref(0)

ref()接收参数,并将其包裹在一个带有.value属性的ref对象中返回:
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1

要在组件模板中访问ref,请从组件的setup()函数中声明并返回他们:
import { ref } from ‘vue’

export default {
// setup是一个特殊的钩子,专门用于组合式API。

setup() {
    const count = ref(0)

    // 将ref暴露给模板
    return {
        count
    }
}

}

{{ count }}

注意,在模板中使用ref时,我们不需要附加.value。为了方便起见,当在模板中使用时,ref会自动解包。

也可以直接在事件监听器中改变一个ref:
<button @click=“count++”>
{{ count }}

对于更复杂的逻辑,我们可以在同一作用域内声明更改的ref的函数,并将它们作为方法与状态一起公开:

import { ref } from ‘vue’

export default {
setup() {
const count = ref(0)

    function increment() {
        //在JavaScript 中需要 .value
        count.value++
    }

    // 不要忘记同时暴露 increment 函数
    return {
        count,
        increment
    }
}

}
然后,暴露的方法可以被用作事件监听器:
<button @click=“increment”>
{{ count }}

< script setup>

在setup() 函数中手动暴露大量的状态和方法非常繁琐。幸运的是,我们可以用过单文件组件(SFC)来避免这种情况。我们可以使用< script setup>来大幅度地简化代码:
< script setup>
import { ref } from ‘vue’
const count = ref(0)
function increment() {
count.value++
}
</ script>

< template>
<button @click=“increment”>
{{ count }}

< /template>

< script setup>中顶层的导入、声明地变量和函数可在统一组件地模板中直接使用。
你可以理解为模板是在同一作用域内声明地一个JavaScript函数–它自然可以访问与他一起声明的所有内容。

当你在模板中使用了一个ref,然后改变了这个ref的值时,Vue会自动检测到这个变化,并且相应地更新DOM。
这是通过一个基于依赖追踪地响应式系统实现的。当一个组件首次渲染时,Vue会追踪在渲染过程中使用地每一个ref。
然后,当一个ref被修改时,他会触发追踪它地组建的一次重新渲染。
在标准的JavaScript中,检测普通变量的访问或修改是行不通的。
然而,我们可以通过getter和setter方法来拦截对象属性的get和set操作。
该.value属性给予了Vue一个机会来检测ref何时被访问或修改。在其内部,Vue在它的getter中执行追踪,在它的setter中执行触发。

另一个ref的好处是,与普通变量不同,你可以将ref传递给函数,同时保留对最新值和响应式连接的访问。当将复杂的逻辑重构为可重用的代码时,这将非常有用。

深层响应性

Ref可以持有任何类型的值,包括深层嵌套的对象、数组或者是JavaScript内置的数据结构,比如Map。
Ref会使他的值具有深层响应性。这意味着即使改变嵌套对象或数组时,变化也会被检测到。
非原始值将通过recative()转换为响应式代理。

DOM更新时机

当响应式状态被修改是,DOM会被自动更新。但是需要注意的是,DOM更新不是同步的。
Vue会在"next tick" 更新周期中缓冲所有状态的修改,以确保不管你进行了多少次状态修改,每个组件都只会被更新一次。

要等待DOM更新完成后再执行额外的代码,可以使用nextTick()全局API:

import { nextTick } from ‘vue’

async function increment() {
count.value++
await nextTick()
// 现在DOM已经更新了
}

reactive()

还有另一种声明响应式状态的方法,即使用reactive() API。与将内部值包装在特殊对象中的ref不同,reactive()将使对象本身具有响应式:
import { reactive } from ‘vue’
const state = reactive({ count: 0 })

在模板中使用:
<button @click=“state.count++”>
{{ state.count }}

响应式对象是JavaScript代理,其行为就和普通对象一样。不同的是,Vue能拦截对响应式对象所有属性的访问和修改,以便进行依赖追踪和触发更新。

reactive()将深层地转换对象:当访问嵌套对象时,他们也会被reactive()包装。当ref的值是一个对象时,ref()也会在内部调用它。与 浅层ref类似,这里也有一个shallowReactive() API可以选择退出深层响应性。

reactive()的局限性

reactive() API有一些局限性:

1 有限的值的类型: 它只能用于对象类型(对象、数组和如 Map、Set这样的集合类型)。它不能持有如 string 、 number或boolean这样的原始类型。
2 不能替换整个对象: 由于Vue的响应式跟踪是通过属性访问实现的,因此我们必须保持对响应式对象的相同引用。这意味着我们不能轻易的替换响应式对象,因为这样的话与第一个引用的响应性连接将丢失:
let state = reactive({ count: 0 })
// 上面的({ count: 0 })引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })
3 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:

const state = reactive({ count: 0 })
// 当解构时,count已经与state.count 断开连接
let { count } = state
// 不会影响原始的state
count++

// 该函数接收到的时一个普通的数字
// 并且无法追踪state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
由于这些限制,我们建议使用ref()作为声明响应式状态的主要API。

额外的ref解包细节

作为reactive对象的属性
一个ref会在作为响应式对象的属性被访问或修改时自动解包。换句话说,它的行为就像一个普通的属性:

const count = ref(0)
const state = reactive({
count
})

console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1

如果将一个新的ref赋值给一个关联了已有ref的属性,那么它会替换掉旧的ref:
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
// 原始ref现在已经和state.count 失去联系

只有当嵌套在一个深层响应式对象内时,才会发生ref解包。当其作为浅层响应式对象的属性被访问时不会解包。

数组和集合的注意事项

与reactive对象不同的是,当ref作为响应式数组或原生集合中的元素被访问时,它不会被解包。

在模板中解包的注意事项

在模板渲染上下文中,只有顶级的ref属性才会被解包。

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值