ref/reactive

作者: 扶不起的蝌蚪 | 来源:发表于2021-08-19 00:16 被阅读0次

    1.reactive

    reactive能够将一个对象经过proxy代理后成为响应式对象返回

    1.1语法

    function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
    
    reactive<object>(target: object): object
    
    • reactive不能包裹基本数据类型,若要包裹,需要外面套一层{},将其转化为对象
    • reactive包裹引用类型,修改原引用数据的值,会影响到proxy代理的响应式对象,但是不会触发视图更新,同样修改proxy代理的响应式对象也会影响到源对象。
    • reactive包裹ref对象,修改ref对象的值,会触发视图更新
    • reactive对象,模板无法自动去掉外层的{},可以同...toRefs进行解构,就可以去掉外层的{}
    • reactive返回的数据不能直接赋值为一个新的对象或者数组,会丢失响应性
    //TS下reactive声明的三种方式
    import { defineComponent, reactive } from 'vue'
     
    interface Student {
      name: string
      class?: string
      age: number
    }
     
    export default defineComponent({
      name: 'HelloWorld',
      setup() {
        const student = reactive<Student>({ name: '阿勇', age: 16 })
        const student: Student = reactive({ name: '阿勇', age: 16 })
        const student = reactive({ name: '阿勇', age: 16, class: 'cs' }) as Student
      }
    })
    

    1.2 reactive包裹数据的三种方式

    • 包裹基本数据类型(出错)
      基本类型(数字、字符串、布尔值)在 reactive 中无法被创建成 proxy 对象,也就无法实现监听。无法实现响应式,特别是在ts中直接就报错了,具体可见
    <template>
      <div>reactive:{{data}}</div>
      <div><button @click="add"> 修改reactive</button></div>
    </template>
    
    <script lang="ts">
    import { reactive, ref, toRefs } from '@vue/reactivity'
    import { defineComponent } from '@vue/runtime-core'
    export default defineComponent({
      name: 'App',
      setup(){
        let value = 0
        let data = reactive<{value:number}>({value})
        const add = ():void=>{
          data.value+= 1
        }
        return {
            data,
            add
        }
      }
    })
    </script>
    
    • 包裹对象数据类型
      模板绑定的时候,由于reactive返回的是proxy对象,因此在模板绑定的时候需要用[对象.属性]的方式进行绑定和修改proxy对象的属性值。
    <template>
      <div>reactiveObj:{{reactiveObj}}</div>
      <div><button @click="addObj"> 修改obj</button></div>
      <div><button @click="addRef"> 修改reactive</button></div>
    </template>
    
    <script lang="ts">
    import { reactive, ref, toRefs } from '@vue/reactivity'
    import { defineComponent } from '@vue/runtime-core'
    export default defineComponent({
      name: 'App',
      setup(){
        let obj = {value:0}
        let reactiveObj = reactive<{value: number}>(obj)
        console.log(obj);
        console.log(reactiveObj);
        const addObj = ():void=>{
          obj.value+= 1
          console.log(obj);
          console.log(reactiveObj);
        }
        const addRef = ():void=>{
            reactiveObj.value+= 1
            console.log(obj);
            console.log(reactiveObj)
        }
        return {
            reactiveObj,
            addObj,
            addRef
        }
      }
    })
    </script>
    

    值得注意的是reactive包裹的数据不能直接赋值为一个新的对象,这种经常会在ajax请求数据返回的时候发生这种错误。

    <template>
        <button @click="change">赋值新对象</button>
        <div>{{ proxyObj.name }}</div>
    </template>
    
    <script setup lang="ts">
    import { reactive } from "@vue/reactivity";
    
    
    let proxyObj = reactive({
        name: "111"
    })
    const change = () => {
        proxyObj = { name: "222" }
        console.log(proxyObj);
    }
    </script>
    
    赋值新对象丢失响应性

    正确的做法是可以对reactive的属性挨个赋值,或者在外面再套一层,然后对内部的数据进行替换赋值、或者利用Object.assign()

    //挨个赋值
    <template>
        <button @click="change">赋值新对象</button>
        <div>{{ proxyObjnew.name }}</div>
    </template>
    
    <script setup lang="ts">
    import { reactive, toRefs } from "@vue/reactivity";
    let proxyObj = reactive({
            name: "111"
    })
    const { data: proxyObjnew } = toRefs(proxyObj)
    const change = () => {
        proxyObj.name="222" 
        console.log(proxyObj);
    }
    </script>
    
    //或者在外面再套一层,然后对内部的数据进行替换赋值
    <template>
        <button @click="change">赋值新对象</button>
        <div>{{ proxyObjnew.name }}</div>
    </template>
    
    <script setup lang="ts">
    import { reactive, toRefs } from "@vue/reactivity";
    let proxyObj = reactive({
        data: {
            name: "111"
        }
    })
    const { data: proxyObjnew } = toRefs(proxyObj)
    const change = () => {
        // proxyObj = { name: "222" }
        proxyObj.data = { name: "222" }
        console.log(proxyObj);
    }
    </script>
    
    //利用Object.assign()
    <template>
        <button @click="change">赋值新对象</button>
        <div>{{ proxyObj.name }}</div>
    </template>
    
    <script setup lang="ts">
    import { reactive, toRefs } from "@vue/reactivity";
    let proxyObj = reactive({
        name: "111"
    })
    const change = () => {
        proxyObj = Object.assign(proxyObj, { name: "222" })
        console.log(proxyObj);
    }
    </script>
    
    <style>
    </style>
    
    • 包裹ref数据
    <template>
      <div>reactiveObj:{{reactiveObj}}</div>
      <div>ref:{{refData}}</div>
      <div><button @click="addObj"> 修改reactive</button></div>
      <div><button @click="addRef"> 修改原ref</button></div>
    </template>
    
    <script lang="ts">
    import { reactive, ref, toRefs } from '@vue/reactivity'
    import { defineComponent } from '@vue/runtime-core'
    export default defineComponent({
      name: 'App',
      setup(){
        let refData = ref<number>(0) 
        console.log(refData);
        //返回
        // {
        //   value:0
        // }
        let reactiveObj = reactive<{value: number}>(refData)
        //返回proxy类型
        // {
        //   value:0
        // }
        //注意不能let reactiveObj = reactive<{value: number}>({refData})
        //你见过这种对象吗?
        // {
        //   {
        //     value:0
        //   }
        // }
        console.log(reactiveObj);
        console.log(refData);
        const addObj = ():void=>{
          reactiveObj.value+= 1
        }
        const addRef = ():void=>{
          refData.value+= 1
        }
        return {
            reactiveObj,
            refData,
            addObj,
            addRef
        }
      }
    })
    </script>
    

    2.ref

    接受一个任意类型的并返回一个响应式且可变的 ref 对象

    2.1语法

    function ref<T>(value: T): Ref<T>
    
    (alias) ref<any>(): Ref<any> (+2 overloads)
    
    
    • ref仅仅是将原数据转化为响应式的Ref对象,其在原数据的外层包裹了一层对象{value:原数据}
    • ref数据在模板渲染过程中会自动提取外层包裹的对象,即提取外层的.value,但是在js代码中修改仍要通过.value才能去修改
    • 对于引用类型的数据而言,由于Ref数据与原数据的引用地址是同一个地址因此ref包裹reactive数据或者引用类型的数据后,再去修改原来的reactive数据或者引用类型的数据,也会影响包裹后的ref数据。
      • 修改引用类型数据,不会触发视图更新
      • 修改reactive数据,会触发视图更新

    2.2 ref包裹数据的三种方式

    • 包裹基本数据类型
      ref包裹基本数据,其只是将基本数据外面再包装了一层对象,所以实质上是
    //Ref<{value:基本数据}>
    Refdata = {
        value:基本数据
    }
    
    <template>
      <div>ref:{{data}}</div>
      <div>reactive:{{data1}}</div>
      <button @click="add"> add</button>
    </template>
    
    <script lang="ts">
    import { reactive, ref, toRefs } from '@vue/reactivity'
    import { defineComponent } from '@vue/runtime-core'
    export default defineComponent({
      name: 'App',
      setup(){
        let data = ref<number>(0)
        let data1 = reactive({value:0})
        console.log(data);
        console.log(data1);
        const add = ():void=>{
          data.value+= 1
          data1.value += 1
        }
        return {
            data,
            data1,
            add
        }
      }
    })
    </script>
    

    经过ref包裹的数据类型,在模板渲染中会自动提取其value,但是由于ref返回的是Ref对象,因此在js中我们修改ref后的值,需要用.value的方式去修改

    • 包裹引用数据类型
      ref包裹引用数据,其只是将引用数据外面再包装了一层对象,所以实质上是
    //Ref<{value:引用数据}>
    Refdata = {
        value:引用数据
    }
    
    <template>
      <div>obj:{{obj}}</div>
      <div>ref:{{data}}</div>
      <div><button @click="addObj"> 修改obj</button></div>
      <div><button @click="addRef"> 修改ref</button></div>
    </template>
    
    <script lang="ts">
    import { reactive, ref, toRefs } from '@vue/reactivity'
    import { defineComponent } from '@vue/runtime-core'
    export default defineComponent({
      name: 'App',
      setup(){
        let obj = {value:0}
        let data = ref<{value: number}>(obj)
        console.log(obj);
        console.log(data);
        const addObj = ():void=>{
          obj.value+= 1
        }
        const addRef = ():void=>{
          data.value.value+= 1
        }
        return {
            obj,
            data,
            addObj,
            addRef
        }
      }
    })
    </script>
    
    ref包裹后,再去修改原对象,无法触发视图更新,但是能够影响到ref对象的值
    • 包裹reactive数据

    ref包裹reactive数据,其只是将reactive数据外面再包装了一层对象,所以实质上是

    //Ref<{value:reactive数据}>
    Refdata = {
        value:reactive数据
    }
    

    在js代码中,我们修改就需要通过Refdata.value.reactive数据键名去修改对应的键值
    但是由于ref在模板绑定的时候,模板会自动提取ref对象的value,所以模板渲染实际上是

    {
        value:reactive数据
    }
    

    但是由于ref仅仅是将数据类型转化为响应式的ref对象,其并不是返回的proxy数据,所以它的引用地址仍然是reactive数据的引用地址,那么修改ref数据会影响到reactive数据,同时修改reactive数据也会影响到ref数据。
    如下例:

    <template>
      <div>ref:{{data}}</div>
      <div>reactive:{{data1}}</div>
      <button @click="add"> add</button>
    </template>
    
    <script lang="ts">
    import { reactive, ref, toRefs } from '@vue/reactivity'
    import { defineComponent } from '@vue/runtime-core'
    export default defineComponent({
      name: 'App',
      setup(){
        let data1 = reactive<{value: number}>({value:0})
        let data = ref<{value: number}>(data1)
        console.log(data1);
        console.log(data);
        const add = ():void=>{
          data.value.value+= 1
         // data1.value += 1
        }
        return {
            data,
            data1,
            add
        }
      }
    })
    </script>
    
    · 修改原reactive对象会影响到ref对象,同时会出发视图更新

    相关文章

      网友评论

        本文标题:ref/reactive

        本文链接:https://www.haomeiwen.com/subject/undwbltx.html