Vue技术

ref/reactive

2021-08-19  本文已影响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包裹数据的三种方式

<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>
<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>
<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<{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<{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对象的值

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对象,同时会出发视图更新
上一篇 下一篇

猜你喜欢

热点阅读