Vue3中ref,reactive,toRef,toRefs, shallowRef ,shallowReactive,toRaw,markRaw等系列

466 阅读6分钟

const 声明的变量不可以被修改,但是 ref 创建对象可以实现数据的响应式更新

JavaScript 中,const 关键字作用是声明常量,用 const 声明的数组不能重新赋值。这意味着 const 声明的常量一旦被赋值后,其不能被重新赋值。然而,在 Vue 的响应式系统中,const 声明的变量可以使用 ref 函数来创建响应式数据,且该响应式数据可以在作用域内被多次修改。这是因为 ref 函数创建的响应式数据是一个特殊的对象,它对内部值进行了封装和代理,从而实现了在修改值时能够触发视图更新的效果。

ref

  • 用法
    主要用来声明基础类型的响应式数据,也可以接受对象类型
  • 描述
    ref用于将一个基础类型的值(String,Number,Boolean,Undefined,Null)转换成一个响应式对象。ref声明的是一个包含value属性的普通对象,可以直接读取和修改value属性的值。使用 ref 来声明一个参数,Vue 会确保这个参数是响应式的,即当其值改变时,任何依赖于这个值的部分都会被重新渲染。
  • 注意
    1. 修改响应式数据不影响以前的数据;数据改变,界面自动更新
    2. 如果参数是对象类型时,其实底层的本质还是reactive,vue自动完成转换
    3. template中访问,系统会自动添加.value;在脚本中需要手动.value
    4. 创建的都是递归响应的,将每一层的json 数据解析成一个proxy对象,类似reactive,区别shallowRefshallowReactive
<template>
   <div>{{num}}</div>  // 直接num便可获取值,不需要.value
</template> 

<script setup lang="ts">
    import { ref,reactive } from 'vue'
    let num = 1
    let num1 = ref(1)// 将num变量包装成响应式
    console.log('num 的值是:', num)
    console.log('num1 的值是:', num1)
    console.log('num1.value 的值是:', num1.value)
    num1.value = 11 // 修改值需要.value
    console.log('num1.value 修改后的值是:', num1.value)
</script>

image.png

reactive

  • 用法
    主要用来声明引用类型的响应式数据, 不接受非Object类型的数据
  • 描述
    Reactive用于将一个普通对象转换成一个响应式对象。reactive返回的是一个响应式对象,可以直接读取和修改对象的属性值
  • 注意
    1. Reactive的本质是将每一层的数都解析成proxy对象,Reactive 的响应式默认都是递归的,改变某一层的值都会递归的调用一遍,重新渲染dom
    2. 创建的都是递归响应的,将每一层的json 数据解析成一个proxy对象,类似ref,区别shallowRefshallowReactive
<template> 
    <div>{{obj.num}}</div> 
</template>
<script setup lang="ts">
    import { ref,reactive } from 'vue'
    let obj = {num: 1}
    let obj1 = reactive({num: 1}) // 将obj变量包装成响应式
    console.log('obj 的值是:', obj)
    console.log('obj1 的值是:', obj1)
    obj1.num = 11 // 修改值
    console.log('obj1修改后的值是:', obj1)
</script>

image.png

toRef

  • 作用
    修改响应式数据会影响以前的数据;数据改变,界面不自动更新
  • 描述
    1. 将对象某一个属性,作为引用返回
    2. 如果使用ref 创建的响应式变量,不会与源数据关联,如果想要与源数据关联但数据更改不更新UI,就要使用toRef创建
<script setup lang="ts">
    import { toRef, reactive } from 'vue'
    // 定义方式,声明普通变量
    let one = {num: 1,name:'渺渺空空'}
    let one1:any = toRef(one, 'num')  // 这东西在这非常的鸡肋,有它没它效果是一样的,有了它后改变成了ObjectRefImpl数据格式
    console.log('one的值:', one);
    console.log('one1的值:', one1);
    console.log('one1.value的值:', one1.value);

    const btnOne = () => {
      one1.value ++
      console.log('one的值:', one);  // 数据发生改变,页面没有更新
      console.log('one1的值:', one1.value); // 数据发生改变,页面没有更新
    }

    // 定义方式,声明响应式变量
    let two = reactive({num: 1,name:'渺渺空空'})
    let two1:any = toRef(two, 'num') // 真没看出来啥作用
    console.log('two的值:', two);
    console.log('two1的值:', two1.value);

    const btnTwo = () => {
      two1.value ++
      console.log('two的值:', two); // 数据发生改变,页面及时更新
      console.log('two1的值:', two1.value);  // 数据发生改变,页面及时更新
    }
</script>
<template>
   <div>
    <span>普通变量</span>
    <span>one.name: {{ one.num }}</span>
    <span>one1:{{ one1 }}</span>
    <button @click="btnOne">修改</button>
  </div>
  <div>
    <span>响应式变量</span>
    <span>one.name: {{ two.num }}</span>
    <span>one1:{{ two1 }}</span>
    <button @click="btnTwo">修改</button>
  </div>
</template> 

image.png

toRefs

  • 作用
    toRefs就是在toRef的基础上,将响应式对象的每个属性都转化为Ref对象,修改每个Ref对象就需要去修改其.value,然后用展开运算符就能始终不失去响应式
<script setup lang="ts">
    import { shallowRef, triggerRef } from 'vue'
    let toRefsObj = reactive({num: 1,name:'渺渺空空'})
    let toRefsObj1:any = toRefs(toRefsObj) // 直接放入对象
    console.log('toRefsObj的值:', toRefsObj);   
    console.log('toRefsObj1的值:', toRefsObj1);  // 可以在控制台看看输出的两个是不一样的

    const btntoRefsObj = () => {
      toRefsObj1.num.value ++
      console.log('toRefsObj的值:', toRefsObj);
      console.log('toRefsObj1的值:', toRefsObj1);
    }
</script>
<template>
   <div>
    <span>toRefs</span>
    <span>toRefsObj.num: {{ toRefsObj.num }}</span>
    <span>toRefsObj1:{{ toRefsObj1 }}</span>
    <button @click="btntoRefsObj">修改</button>
  </div>
</template>

image.png

shallowRef

  • 作用
    创建的是非递归的响应对象,类似shallowReactive,区别refReactive
  • 描述
    1. 通过shallowRef创建的响应式对象,需要修改整个value才能重新渲染dom
    2. 如果使用了shallowRef想要只更新某一层的数据可以使用triggerRef
<script setup lang="ts">
    import { shallowRef, triggerRef } from 'vue'
    let shallowRefobj = shallowRef({ num: 1, name: '渺渺空空' }) // 将shallowRefobj变量包装成响应式
    console.log('shallowRefobj的值:', shallowRefobj);
    console.log('shallowRefobj.value的值:', shallowRefobj.value);
    // 方式1
    shallowRefobj.value.num = 11 // 此时不会触发页面更新, 此处和ref一致,需要.value
    console.log('方式1shallowRefobj.value的值:', shallowRefobj.value);
    triggerRef(shallowRefobj) // 此时页面就会重新渲染
    // 方式2
    shallowRefobj.value = { num: 11, name: '渺渺空空' } // 此时会触发页面更新
    console.log('方式2shallowRefobj.value的值:', shallowRefobj.value);
</script>

image.png

shallowReactive

  • 作用
    1. 创建的是非递归的响应对象,改变第一层的数据会导致页面重新渲染
  • 描述
    1. 创建的是非递归的响应对象,类似shallowReactive,区别refReactive
<script setup lang="ts">
    import { shallowReactive } from 'vue'
    let obj = shallowRef({num: 1,info: {name:'渺渺空空'}}) // 将obj变量包装成响应式
    // 方式1
    obj.num = 11 // 此时会触发页面更新
    // 方式2
    obj.info.name = 'aaa' // 此时不会触发页面更新
</script>

toRaw

  • 作用
    如果只想修改响应式的数据不想引起页面渲染可以使用toRaw这个方法
  • 描述

    只修改数据不渲染页面

<script setup lang="ts">
    import { toRaw, reactive } from 'vue'
    let toRawObj = reactive({num: 1,name:'渺渺空空'})
    let toRawObj1:any = toRaw(toRawObj) // 直接放入对象
    console.log('toRawObj的值:', toRawObj);   
    console.log('toRawObj1的值:', toRawObj1);  // 可以在控制台看看输出的两个是不一样的

    const btntoRawObj = () => {
      toRawObj1.num.value ++
      console.log('toRawObj的值:', toRawObj);
      console.log('toRawObj1的值:', toRawObj1);
    }
</script>
<template>
   <div>
    <span>toRaw</span>
    <span>toRawObj.num: {{ toRawObj.num }}</span>
    <span>toRawObj1:{{ toRawObj1 }}</span>
    <button @click="btntoRawObj">修改</button>
  </div>
</template>

image.png

markRaw

  • 作用
    让数据变不成响应式数据
    let markRawObj = markRaw({ num: 1, name: '渺渺空空' })
    console.log('markRawObj的值:', markRawObj)
    let markRawObj2 = reactive(markRawObj)  // 此时markRawObj2已经是非响应数据了
    console.log('markRawObj2的值:', markRawObj2)
    markRawObj.num = 11
    console.log('markRawObj修改的值:', markRawObj)
    markRawObj2.num = 11
    console.log('markRawObj2修改的值:', markRawObj)

image.png

customRef

  • 作用
    通过customRef这个方法可以自定义一个响应式的ref方法
function mycustomRef(value){
   /*
    customRef函数返回一个对象,对象里面有2个方法,get/set方法,创建的对象获取数据的时候能 访问到get方法,创建的对象修改值的时候会触发set 方法
    customRef函数有2个参数,track/trigger,track参数是追踪的意思,get 的方法里面调用,可以随时追踪数据改变。trigger参数 是触发响应的意思,set 方法里面调用可以更新UI界面
   */
    return customRef((track,trigger)=>{
       return {
          get(){
             track()//追踪数据
             return value     
          },
          set(newVal){
             value = newVal
             trigger()//更新UI界面
          }
       }
    })

}

setup(){
   var num = myRef(1)
   num.value = 11
}