Vue3-02-ref/reactive/toRef/toRefs

8/27/2021 Vue3

ref、reactive、toRef、toRefs响应式数据

  • ref、reactive、toRef、toRefs 这些方法 均需从vue中导入到组件。
  • ref、reactive、toRef、toRefs 创建的变量在 setup 中都需要return { ... }抛出才可在页面使用,除非你是为props创建响应式。

参考 (opens new window)

# ref() 和 reactive()

  • ref用来定义:基本类型数据
  • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
  • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value
  • reactive用来定义:对象或数组类型数据
  • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源代码内部的数据。
  • reactive定义的数据:操作数据与读取数据:均不需要.value

ref可以定义对象或数组的,它只是内部自动调用了reactive来转换

<template>
  <div><h3>{{ temp }}</h3><p>{{ user.name }}</p><p>{{ user.age }}</p>
    <button @click="increase">click me!</button>
  </div>
</template>

import { ref, reactive } from 'vue'
export default {
  setup() {
    const temp = ref(0)
    temp.value = 'hello'
    const user = reactive({ name: 'lemon', age: 20 })
    console.log(temp)
    console.log(temp.value) // hello
    console.log(user) // Proxy {name:'lemon',age:20}

    const increase = () => { user.age++ }
    return { temp, user, increase }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

reactive()函数可以代理一个复杂数据类型比如:对象、数组,但不能代理基本类型值,例如字符串、数字、boolean 等, 用ref()函数来间接对基本类型值进行处理。

  • ref的本质还是reactive系统会自动根据ref()函数的入参将其转换成ref(x)reactive({value:x})

  • ref(user.name)就相当于ref('lemon')也相当于reactive({value:'lemon'})

  1. 在 vue 模板中使用ref的值不需要通过value属性获取(vue 会自动给 ref 的值加上.value)
  2. 在 js 中使用ref的值要通过.value获取

# toRef() 和 toRefs()

toRef是将个对象 A 中的某个属性 x 转换为响应式数据, 其接收两个参数,第一个参数为对象 A,第二个参数为对象中的某个属性名 x。

import { toRef } from 'vue'
export default {
  setup() {
    const user = { name: 'lemon', age: 3 }
    const userRef = toRef(user, 'age')
    return { userRef }
  }
}
1
2
3
4
5
6
7
8
<script lang="ts">
import { ref, toRef } from "vue";
export default {
  setup() {
    const temp = { count: 1 };
    const temp0 = 1;
    const state0 = ref(temp0);
    const state1 = ref(temp.count);
    const state2 = toRef(temp, "count");

    const add0 = () => {
      state0.value++;
      console.log("原始值:", temp0); //原始值:1
      console.log("响应式数据对象ref:", state0.value); //响应式数值ref:2
    };
    const add1 = () => {
      state1.value++;
      console.log("原始值:", temp); //原始值:1
      console.log("响应式数据对象ref:", state1.value); //响应式对象ref:2
    };
    const add2 = () => {
      state2.value++;
      console.log("原始值:", temp); // 原始值:2
      console.log("响应式数据对象toRef:", state2.value); //响应式对象toRef:2
    };
    return { state0, state1, state2, add0, add1, add2 };
  },
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# ref()和 toRef()有以下区别

  1. 参数不同:ref()接收一个 js 基本数据类型的参数;toRef()接收两个参数,第一个为对象,第二个为对象中的某个属性;
  2. 原理不同:ref()是对原数据的一个深拷贝,当其值改变时不会影响到原始值;toRef()是对原数据的一个引用,当值改变时会影响到原始值;
  3. 响应性不同:ref()创建的数据会触发 vue 模版更新;toRef()创建的响应式数据并不会触发 vue 模版更新,所以toRef()的本质是引用,与原始数据有关联。

# toRefs()

  • 对象的多个属性都变成响应式数据,并且要求响应式数据和原始数据相关联,并且更新响应式数据时不更新界面,这时候用toRefs()

  • 对响应式对象进行 toRefs 后,可以对其进行解构方便 vue 模版使用,但是不会使其失去响应性。 reactive()响应式数据通过js结构之后,会丢失响应

setup() {
    const state = reactive({ foo: 1, bar: 2 }) // 创建响应式对象state
    const stateAsRefs = toRefs(state) // 将响应式的对象变为普通对象结构
    state.foo++
    console.log(stateAsRefs.foo.value) // 2

    stateAsRefs.foo.value++
    console.log(state.foo) // 3
    return { ...stateAsRefs };
}
1
2
3
4
5
6
7
8
9
10

toRefs()将响应式的对象 state 变为普通对象 stateAsRefs 后, return 时使用 ES6 的扩展运算符,在模版中可以直接使用其内部属性,且仍具有响应性 (对响应式对象 state 使用扩展运算符后,其内部属性就失去了响应性 )

vue3_ref

# 响应式判断

isRef: 检查值是否为一个 ref 对象。 isReactive:检查对象是否是由 reactive 创建的响应式代理。 isReadonly: 检查对象是否是由 readonly 创建的只读代理。 isProxy:检查对象是否是由 reactive 或 readonly 创建的 proxy。

import {ref, reactive,readonly,isRef,isReactive,isReadonly,isProxy } from 'vue'
export default {
  name:'App',
  setup(){
    let fullName = reactive({name:'小唐',price:'20k'})
    let num = ref(0)
    let fullNames = readonly(fullName)
    console.log(isRef(num))
    console.log(isReactive(fullName))
    console.log(isReadonly(fullNames))
    console.log(isProxy(fullName))
    console.log(isProxy(fullNames))
    console.log(isProxy(num))
    return {}
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16