响应式对象toRef

使用toRef创建一个对象使其value子属性与源底层属性相互响应,如下:

<template>
 <h1 v-text="state"></h1>
 <h1 v-text="obj.name"></h1>
</template>
<script setup>
import { toRef } from 'vue';
  let obj = {name:5, age:18}
  let state = toRef(obj, 'name')
</script>

1、toRef函数的书写规范:toRef(参数1,"参数2")

  • toRef 的 R 是大写
  • 参数1是要绑定的父层。
  • 参数2是要暴露的子属性的KEY,如是对象KEY是属性名用引号括起,如是数组KEY是索引号(数组KEY可以不用引号括起)。

2、toRef函数的内部原理

toRef会用ObjectRefImpl包装父层成一个对象。

  • _object对象与父层全等
  • _key属性定义要暴露的子属性
  • value属性存储要暴露的值
  • Prototype原型集,内含get value与set value函数,监测value值负责与父层的相互响应。

3、相互响应

  • 父层相互响应: 新变量的_object对象与源父层全等,使用同一指针,引用同一对象,两者相互响应
  • 父层失去响应:
    当_object对象重新赋值为基本类型时(_object改用栈内存固定地址)两者失去响应,且value值为空;
    当_object对象重新赋值一个新对象时(引用对象不同)两者失去响应,且value值为空,通过修改_key值重新定义要暴露的子属性,使value拥有新值。
    当父层重新赋值为基本类型或赋值一个新对象时,两者失去响应,toRef的_object对象、_key等还是原值,依旧能照常使用。
  • 子层响应: 通过get value与set value函数监测value值,value更新时,_object对象的_key值子属性同步更新,value与父层_key值子属性两者相互响应。

4、调用

暴露值包装在value属性里,因此调用值时需要对value解包。
逻辑部可以调用所有属性,模版部只能调用value属性。
逻辑部调用value属性: 需要加.value解包。
模版部调用: 如果是顶级属性自动解包(不需要加.value解包)、非顶级属性且不需计算会自动解包、非顶级属性需计算需要加value解包。

5、操作_object与_key

  • 通过操作_object属性可操作父层。
  • 通过操作_key属性可更改要暴露的子属性。
    如下:

    <template>
    <h1 v-text="state"></h1>
    <h1 v-text="obj.name"></h1>
    <button @click="ee">测试_object</button>
    <button @click="ef">测试_key</button>
    </template>
    <script setup>
     import { toRef } from 'vue';
     let obj = {name:5, age:18}
     let state = toRef(obj, 'name')
     function ee(){state._object.name='我通过_object改变',console.log(obj.name)}
     function ef(){state._key='age',console.log(state.value)}
    </script>

6、toRef与普通赋值的区别

  • toRef只能赋值非底层属性
    toRef只能赋值非底层属性,赋值后变量的_object对象与源父层同等。与普通赋值非底层属性一样拥有与源的相互响应性。toRef不能赋值底层属性,普通赋值能赋底层属性,新变量与源无相互响应性。
  • toRef只暴露value子属
    toRef只向DOM暴露value子属性以供DOM调用,普通赋值向DOM暴露所有子属性以供DOM调用。
  • 伪赋值底层属性
    toRef子属性value与父层下的底层属性相互响应,DOM调用value属性时不用加value解包,看上去就象底层属性赋值给toRef变量一样。他能做到普通赋值底层属性不能做到的与源相互响应。

    <template>
    <button @click="ee">测试引用型赋值响应</button>
    <button @click="ef">测试基本型赋值响应</button>
    <button @click="ed">测试toRef的子属性响应</button>
    </template>
    <script setup>
     import { toRef } from 'vue';
     let obj = {name:{ttt:5, age:18}}
     let state = toRef(obj.name, 'ttt')
     let stat = obj.name
     let sta = obj.name.ttt
     function ee(){stat.ttt='引用型赋值响应1',console.log(obj.name),obj.name.ttt='引用型赋值响应2',console.log(stat)}//  有响应性
    function ef(){sta='基本型赋值响应1',console.log(obj.name),obj.name.ttt='基本型赋值响应2',console.log(sta)}//  无响应性
     function ed(){state.value='toRef的子属性响应1',console.log(obj.name),obj.name.ttt='toRef的子属性响应2',console.log(state)}//  有响应性
    </script> 

7、toRef专为响应性而生

不使用toRef赋值reactive的底层属性
新变量不继承Proxy代理包装,新变量与底层属性不相互响应,新变量不具DOM响应性。
使用toRef赋值reactive的底层属性
新变量会用ObjectRefImpl包装父层,父层继承Proxy代理,对外使用value子属性暴露这个底层属性,DOM调用value属性时不用加value解包,造成象toRef赋值reactive的底层属性的假象,新变量的value子属性与底层属性具有了相互响应,具有了DOM响应性。

(批量toRef)---toRefs

使用解构的方法,解构出的对象的子属性与源底层属性相互响应,如下:

<template>
  <h1 v-text="name"></h1>
  <h1 v-text="wowo"></h1>
  <button @click="ee">我来改变</button>  
</template>
<script setup>
import { reactive,toRefs } from 'vue';
let obj ={name:5,wowo:8}
   let{name,wowo} = toRefs(obj)
   function ee(){console.log(name,wowo)     
          obj.name={yy:88}}
</script>

1、toRefs函数的书写规范:{KEY1,KEY2}=toRefs(参数1)

toRefs 的 R 是大写
参数1是要绑定的父层。
遵循解构书写规范,解构对象型(新变量与源子属性同名且放在{}内),解构数组型(新变量可随意命名且放在[]内)。

2、toRefs函数的内部原理

解构出的每个变量都是用ObjectRefImpl包装的一个对象。
对象的_object子对象与父层全等
对象的_key子属性定义要暴露的子属性,value属性存储要暴露的值
Prototype原型集,内含get value与set value函数,监测value值负责与父层的相互响应。

3、相互响应

变量与源父层的相互响应性: 同toRef一样。
同构变量相互响应性: 因为他们都绑定同一个父层,通过操作_object可以改变其它同构变量值,更改_key值可以使自已成为其它同构变量,成为其他同构变量后与这个其它同构变量同等(拥有同样的_key值与value值)。如下:

<template>
  <button @click="ee">改变对方</button>
  <button @click="ef">成为对方</button> 
  <button @click="ed">成为对方</button> 
</template>
<script setup>
import {toRefs } from 'vue';
let obj ={name:'我是name',wowo:'我是wowo'}
   let {name,wowo}= toRefs(obj)
   function gw(){obj.name='我是name',obj.wowo='我是wowo'}
   function ee(){gw(),name._object.wowo='通过_object改变wowo',console.log(wowo.value) }
   function ef(){gw(),name._key='wowo',console.log('通过_key,name成了',name.value) }
   function ed(){gw(),name._key='wowo',name.value="name成了wowo后,可通过value改变wowo",console.log(wowo.value)}
</script>

4、调用

同toRef

5、toRefs专为响应性而生

不使用toRefs解构reactive的底层属性
解构出来的新变量不继承Proxy代理包装,新变量与赋值层不相互响应,不具DOM响应性。
使用toRefs解构reactive的底层属性
解构出来的新变量会用ObjectRefImpl包装父层并对外暴露这个底层属性,父层继承Proxy代理包装,新变量的value子属性与这个底层属性相互响应,并具有了DOM响应性。


百分之一百零八
15 声望3 粉丝