vue3 学习4 - reactive函数

87 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 26 天,点击查看活动详情

start

  • 上文学习了 vue3 中的 ref 函数;
  • 官方有这么一句话 "ref() 当值为对象类型时,会用 reactive() 自动转换它的 .value。"
  • 今天就来学习一下 reactive()

开始

1. 基础知识

解释:

返回一个对象的响应式代理。

基础使用:

<template>
  <div>
    reactive学习
    {{ obj.count }}
    <br />
    <button @click="add">添加</button>
  </div>
</template>

<script>
import { reactive } from 'vue'
export default {
  setup() {
    const obj = reactive({ count: 0 })

    function add() {
      obj.count++
    }
    return {
      obj,
      add,
    }
  },
}
</script>

总结:

  • reactive() 参数接受一个对象。返回一个响应式对象。
  • 响应式的对象 : Proxy {count: 0}

2. 特殊的传参

上一个小节的演示代码是传入一个普通的对象。那么我们传入其他类型的数据会如何?

1. 基础类型数据:

<script>
import { reactive } from 'vue'
export default {
  setup() {
    const obj = reactive({ count: 0 })
    console.log(obj)

    let num = reactive(0)
    console.log('num', num) // num 0

    let str = reactive('tomato')
    console.log('str', str) // str tomato

    let bean = reactive(false)
    console.log('bean', bean) // bean false

    let un = reactive(undefined)
    console.log('un', un) // un undefined

    let nu = reactive(null)
    console.log('nu', nu) // nu null

    let sym = reactive(Symbol('hello'))
    console.log('sym', sym) // sym Symbol(hello)

    let bigNum = reactive(10n)
    console.log('bigNum', bigNum) // bigNum 10n

    /* 还会有这么一个警告 */
    /* value cannot be made reactive */
  },
}
</script>

image.png

2. 向 reactive() 传入 ref() 返回的对象

2.1 普通的传入 --- 会解包

<script>
import { ref, reactive } from 'vue'
export default {
  setup() {
    // 1. 获得一个 RefImpl 的实例对象
    const count = ref(1) // { value : 1 }

    // 2. 获得一个 Proxy 的实例对象
    const obj = reactive({ count }) // { count:{ value : 1 } }

    // 3. 比较两个值,居然不相等 ?
    console.log(count.value === obj.count.value) // false

    // 4. reactive() 将深层地解包任何 ref 属性,同时保持响应性。
    console.log(count.value === obj.count) // true

    // 5. 修改 ref 的返回值,也会更新 `obj.count`
    count.value++
    console.log(count.value) // 2
    console.log(obj.count) // 2

    // 6.修改 reactive 的返回值,也会更新 `count` ref
    obj.count++
    console.log(obj.count) // 3
    console.log(count.value) // 3
  },
}
</script>

2.2 传入 响应式数组map中的ref()

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

2.3 将一个 ref 赋值给为一个 reactive 属性,会被解包

const count = ref(1)
const obj = reactive({})

obj.count = count

console.log(obj.count) // 1
console.log(obj.count === count.value) // true

总结

reactive() 传入 ref() 的返回值:

  1. 对象中的 ref() 会解包;
  2. 数组和 map 结构中的 ref() ,不会解包;
  3. reactive 属性赋值,会解包;

3. 其他注意事项:

  1. 响应式转换是“深层”的:它会影响到所有嵌套的属性。

    <template>
      <div>
        <div>
          {{ obj1.a }}
          {{ obj1.b.c.d }}
          <button @click="add">增加</button>
        </div>
      </div>
    </template>
    
    <script>
    import { reactive } from 'vue'
    export default {
      setup() {
        var obj1 = reactive({ a: 1, b: { c: { d: 1 } } })
        function add() {
          obj1.a++
          obj1.b.c.d++
        }
    
        return {
          obj1,
          add,
        }
      },
    }
    </script>
    
    
  2. reactive() 返回的是一个原始对象的 Proxy ,它和原始对象是不相等的

    let obj = { a: 1 }
    let obj1 = reactive(obj)
    
    console.log(obj === obj1) // false
    console.log(obj1 === reactive(obj)) // true
    
  3. reactive() API 有两条限制:

    • 仅对对象类型有效(对象、数组和 MapSet 这样的集合类型),而对 stringnumberboolean 这样的原始类型无效。

    • 因为 Vue 的响应式系统是通过属性访问进行追踪的,因此我们必须始终保持对该响应式对象的相同引用。这意味着我们不可以随意地“替换”一个响应式对象,因为这将导致对初始引用的响应性连接丢失:

      const state = reactive({ count: 0 })
      
      // n 是一个局部变量,同 state.count
      // 失去响应性连接
      let n = state.count
      // 不影响原始的 state
      n++
      
      // count 也和 state.count 失去了响应性连接
      let { count } = state
      // 不会影响原始的 state
      count++
      

总结:

  1. reactive() 参数支持 对象、数组和 MapSet类型;
  2. 返回一个响应式的 Proxy 实例;
  3. 传入 ref() 会自动解包,(不包括 数组 map结构)

疑惑:

  • Proxy 是什么?