vue3中的ref、reactive怎么使用


本篇内容主要讲解“vue3中的ref、reactive怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“vue3中的ref、reactive怎么使用”吧! 在 Vue3 中我们可以使用 reactive() 创建一个响应式对象或数组:

import{reactive}from'vue'

conststate=reactive({count:0})

这个响应式对象其实就是一个 Proxy, Vue 会在这个 Proxy 的属性被访问时收集副作用,属性被修改时触发副作用。要在组件模板中使用响应式状态,需要在 setup() 函数中定义并返回。

当然,也可以使用 中顶层的导入和变量声明可以在模板中直接使用。

reactive() 返回的是一个原始对象的 Proxy,他们是不相等的:

constraw={}
constproxy=reactive(raw)

console.log(proxy===raw)//false

原始对象在模板中也是可以使用的,但修改原始对象不会触发更新。因此,要使用 Vue 的响应式系统,就必须使用代理。

为保证访问代理的一致性,对同一个原始对象调用 reactive() 会总是返回同样的代理对象,而对一个已存在的代理对象调用 reactive() 会返回其本身:

constraw={}
constproxy1=reactive(raw)
constproxy2=reactive(raw)

console.log(proxy1===proxy2)//true

console.log(reactive(proxy1)===proxy1)//true

这个规则对嵌套对象也适用。依靠深层响应性,响应式对象内的嵌套对象依然是代理:

constraw={}
constproxy=reactive({nested:raw})
constnested=reactive(raw)

console.log(proxy.nested===nested)//true

在 Vue 中,状态默认都是深层响应式的。但某些场景下,我们可能想创建一个 浅层响应式对象 ,让它仅在顶层具有响应性,这时候可以使用 shallowReactive()

conststate=shallowReactive({
foo:1,
nested:{
bar:2
}
})

//状态自身的属性是响应式的
state.foo++

//下层嵌套对象不是响应式的,不会按期望工作
state.nested.bar++

注意:浅层响应式对象应该只用于组件中的根级状态。避免将其嵌套在深层次的响应式对象中,因为其内部的属性具有不一致的响应行为,嵌套之后将很难理解和调试。reactive() 虽然强大,但也有以下几条限制:仅对对象类型有效(对象、数组和 MapSet 这样的集合类型),而对 stringnumberboolean 这样的原始类型无效。因为 Vue 的响应式系统是通过属性访问进行追踪的,如果我们直接“替换”一个响应式对象,这会导致对初始引用的响应性连接丢失:

将响应式对象的属性赋值或解构至本地变量,或是将该属性传入一个函数时,会失去响应性:

为了解决以上几个限制,ref 闪耀登场了!Vue 提供了一个 ref() 方法来允许我们创建使用任何值类型的响应式 ref 。ref() 将传入的参数包装为一个带有 value 属性的 ref 对象:

import{ref}from'vue'

constcount=ref(0)

console.log(count)//{value:0}

count.value++
console.log(count.value)//1

和响应式对象的属性类似,ref 的 value 属性也是响应式的。同时,当值为对象类型时,Vue 会自动使用 reactive() 处理这个值。一个包含对象的 ref 可以响应式地替换整个对象:

ref 从一般对象上解构属性或将属性传递给函数时,不会丢失响应性:参考 前端进阶面试题详细解答

conststate={
count:ref(0)
}
//解构之后,和state.count依然保持响应性连接
const{count}=state
//会影响state
count.value++

//该函数接收一个ref,和传入的值保持响应性连接
functioncallSomeFunction(count){
//会影响state
count.value++
}
callSomeFunction(state.count)

ref() 让我们能创建使用任何值类型的 ref 对象,并能够在不丢失响应性的前提下传递这些对象。这个功能非常重要,经常用于将逻辑提取到 组合式函数 中。

//mouse.js
exportfunctionuseMouse(){
constx=ref(0)
consty=ref(0)

//...
return{x,y}
}

所谓解包就是获取到 ref 对象上 value 属性的值。常用的两种方法就是 .valueunref()unref() 是 Vue 提供的方法,如果参数是 ref ,则返回 value 属性的值,否则返回参数本身。当 ref 在模板中作为顶层属性被访问时,它们会被自动解包,不需要使用 .value 。下面是之前的例子,使用 ref() 代替:

还有一种情况,如果文本插值({{ }})计算的最终值是 ref ,也会被自动解包。下面的非顶层属性会被正确渲染出来。

其他情况则不会被自动解包,如:object.foo 不是顶层属性,文本插值({{ }})计算的最终值也不是 ref:

constobject={foo:ref(1)}

下面的内容将不会像预期的那样工作:

{{object.foo+1}}

渲染的结果会是 [object Object]1,因为 object.foo 是一个 ref 对象。我们可以通过将 foo 改成顶层属性来解决这个问题:

constobject={foo:ref(1)}
const{foo}=object

{{foo+1}}

现在结果就可以正确地渲染出来了。当一个 ref 被嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包,因此会表现得和一般的属性一样:

constcount=ref(0)
conststate=reactive({count})

console.log(state.count)/免费云主机域名/0

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

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

constcount=ref(0)
conststate=shallowReactive({count})

console.log(state.count)//{value:0}而不是0

如果将一个新的 ref 赋值给一个已经关联 ref 的属性,那么它会替换掉旧的 ref:

constcount=ref(1)
conststate=reactive({count})

constotherCount=ref(2)
state.count=otherCount

console.log(state.count)//2
//此时count已经和state.count失去连接
console.log(count.value)//1

跟响应式对象不同,当 ref 作为响应式数组或像 Map 这种原生集合类型的元素被访问时,不会进行解包。

constbooks=reactive([ref('Vue3Guide')])
//这里需要.value
console.log(books[0].value)

constmap=reactive(newMap([['count',ref(0)]]))
//这里需要.value
console.log(map.get('count').value)

toRef 是基于响应式对象上的一个属性,创建一个对应的 ref 的方法。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。

conststate=reactive({
foo:1,
bar:2
})

constfooRef=toRef(state,'foo')

//更改源属性会更新该ref
state.foo++
console.log(fooRef.value)//2

//更改该ref也会更新源属性
fooRef.value++
console.log(state.foo)//3

toRef() 在你想把一个 prop 的 ref 传递给一个组合式函数时会很有用:

toRef 与组件 props 结合使用时,关于禁止对 props 做出更改的限制依然有效。如果将新的值传递给 ref 等效于尝试直接更改 props,这是不允许的。在这种场景下,你可以考虑使用带有 getsetcomputed 替代。注意:即使源属性当前不存在,toRef() 也会返回一个可用的 ref。这让它在处理可选 props 的时候非常有用,相比之下 toRefs 就不会为可选 props 创建对应的 refs 。下面我们就来了解一下 toRefstoRefs() 是将一个响应式对象上的所有属性都转为 ref ,然后再将这些 ref 组合为一个普通对象的方法。这个普通对象的每个属性和源对象的属性保持同步。

conststate=reactive({
foo:1,
bar:2
})

//相当于
//conststateAsRefs={
//foo:toRef(state,'foo'),
//bar:toRef(state,'bar')
//}
conststateAsRefs=toRefs(state)

state.foo++
console.log(stateAsRefs.foo.value)//2

stateAsRefs.foo.value++
console.log(state.foo)//3

从组合式函数中返回响应式对象时,toRefs 相当有用。它可以使我们解构返回的对象时,不失去响应性:

//feature.js
exportfunctionuseFeature(){
conststate=reactive({
foo:1,
bar:2
})

//...
//返回时将属性都转为ref
returntoRefs(state)
}

toRefs 只会为源对象上已存在的属性创建 ref。如果要为还不存在的属性创建 ref,就要用到上面提到的 toRef。以上就是 ref、reactive 的详细用法,不知道你有没有新的收获。接下来,我们来探讨一下响应式原理。大家都知道 Vue2 中的响应式是采⽤ Object.defineProperty() , 通过 getter / setter 进行属性的拦截。这种方式对旧版本浏览器的支持更加友好,但它有众多缺点:初始化时只会对已存在的对象属性进行响应式处理。也是说新增或删除属性,Vue 是监听不到的。必须使用特殊的 API 处理。数组是通过覆盖原型对象上的7个⽅法进行实现。如果通过下标去修改数据,Vue 同样是无法感知的。也要使用特殊的 API 处理。无法处理像 MapSet 这样的集合类型。带有响应式状态的逻辑不方便复用。针对上述情况,Vue3 的响应式系统横空出世了!Vue3 使用了 Proxy 来创建响应式对象,仅将 getter / setter 用于 ref ,完美的解决了上述几条限制。下面的代码可以说明它们是如何工作的:

functionreactive(obj){
returnnewProxy(obj,{
get(target,key){
track(target,key)
returntarget[key]
},
set(target,key,value){
target[key]=value
trigger(target,key)
}
})
}

functionref(value){
constrefObject={
getvalue(){
track(refObject,'value')
returnvalue
},
setvalue(newValue){
value=newValue
trigger(refObject,'value')
}
}
returnrefObject
}

不难看出,当将一个响应性对象的属性解构为一个局部变量时,响应性就会“断开连接”。因为对局部变量的访问不会触发 get / set 代理捕获。我们回到响应式原理。在 track() 内部,我们会检查当前是否有正在运行的副作用。如果有,就会查找到存储了所有追踪了该属性的订阅者的 Set,然后将当前这个副作用作为新订阅者添加到该 Set 中。

//activeEffect会在一个副作用就要运行之前被设置
letactiveEffect

functiontrack(target,key){
if(activeEffect){
consteffects=getSubscribersForProperty(target,key)
effects.add(activeEffect)
}
}

副作用订阅将被存储在一个全局的 WeakMap>> 数据结构中。如果在第一次追踪时没有找到对相应属性订阅的副作用集合,它将会在这里新建。这就是 getSubscribersForProperty() 函数所做的事。在 trigger() 之中,我们会再次查找到该属性的所有订阅副作用。这一次我们全部执行它们:

functiontrigger(target,key){
consteffects=getSubscribersForProperty(target,key)
effects.forEach((effect)=>effect())
}

这些副作用就是用来执行 diff 算法,从而更新页面的。到此,相信大家对“vue3中的ref、reactive怎么使用”有了更深的了解,不妨来实际操作一番吧!这里是百云主机网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

相关推荐: javascript如何实现正则替换

本篇内容主要讲解“javascript如何实现正则替换”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小免费云主机域名编来带大家学习“javascript如何实现正则替换”吧! javascript实现正则替换的两种方法:1、使用re…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 05/17 21:02
下一篇 05/18 09:37

相关推荐