vue3 reactive全家桶

55 阅读2分钟

reactive

源码位置:packages/reactivity/src/reactive.ts

类型:

function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

它只能用于对象类型 (对象、数组和如 MapSet 这样的集合类型)。它不能持有如 stringnumber 或 boolean 这样的原始类型

响应式转换是“深层”的:它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性,同时保持响应性。

  • reactiveref结合时,ref自动解包情况:访问reactive对象的ref属性值时,ref的自动解包以及响应式
const age = ref(1)
const obj = reactive({
   people: {
       age
   } 
})

// 会对ref属性值进行深层解包
console.log('obj.people.age === age.value:', obj.people.age === age.value) // true

// 先点击print按钮
const print = () => {
    const { age } = obj.people

    console.log('解构出来的age值:', age) // 1
    console.log('obj.people.age:', obj.people.age) // 1
  }

  // 再点击change按钮
  const change = () => {
    obj.people.age++
    console.log('obj.people.age:', obj.people.age) // 2
    console.log('age.value:', age.value) // 2

    age.value++
    console.log('obj.people.age:', obj.people.age) // 3
    console.log('age.value:', age.value) // 3
  }
<template>
  <div>
    obj.people.age: {{ obj.people.age }}
  </div>

  <button @click="print">打印年龄</button>
  <button @click="change">修改年龄</button>

</template>7 
  • reactiveref结合时,ref不自动解包情况:当访问到某个响应式数组或 Map 这样的原生集合类型中的 ref 元素时,不会执行 ref 的解包,详细使用方法见:juejin.cn/post/730657…

若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,可以使用shallowReactive来替代。

为reactive()标注类型

  1. 隐式地从参数中推倒
import { reactive } from 'vue'

// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue 3 指引' })
  1. 在定义变量时,显示地标注类型的返回值
import { reactive } from 'vue'

interface Book {
  title: string
  year?: number
}

const book: Book = reactive({ title: 'Vue 3 指引' })
  const age = ref(1)
  
  // 不推荐使用,在接口中定义的age为Ref<number>类型,但是在访问obj1.people.age时,返回的数据类型为number类型
  const obj1 = reactive<{
    people: {
      age: Ref<number>
    }
  }>({
    people: {
      age
    }
  })

  interface People {
    people: {
      age: number
    }
  }
  
  // age赋值为Ref<number>
  const obj2: People = reactive({
    people: {
      age
    }
  })

  // age赋值为number类型
  const obj3: People = reactive({
    people: {
      age: 1
    }
  })

  console.log(obj1.people.age) // number 1
  console.log(obj2.people.age) // number 1
  console.log(obj3.people.age) // number 1

不推荐使用 reactive() 的泛型参数,因为处理了深层次 ref 解包的返回值与泛型参数的类型不同。

shallowReactive

类型:

function shallowReactive<T extends object>(target: T): T

和 reactive() 不同,这里没有深层级的转换:一个浅层响应式对象里只有根级别的属性是响应式的。属性的值会被原样存储和暴露,这也意味着值为 ref 的属性不会被自动解包了。

const state = shallowReactive({
    count: 2, // 响应式
    foo: ref(1),
    nested: {
      bar: 2 // 非响应式
    }
  })

  console.log('state.foo:', state.foo) // 打印Ref<1>,而非1
  <!-- 自动解包,为插值语法的特殊情况 -->
  <div>
    {{ state.foo}}
  </div>

  <!-- 不会自动解包,页面显示为:[object Object]1 -->
  <div>
    {{ state.foo + 1}}
  </div>
const reactiveState = reactive({
    foo: ref(1),
    nested: {
      bar: 2
    }
  })
<!-- 1 -->
<div>{{ reactiveState.foo }}</div>
<!-- 2 -->
<div>{{ reactiveState.foo + 1 }}</div>

浅层数据结构应该只用于组件中的根级状态。请避免将其嵌套在深层次的响应式对象中,因为它创建的树具有不一致的响应行为,这可能很难理解和调试。