ref和reactive

459 阅读4分钟

ref

ref处理基本类型

    import { ref } from 'vue'
    const count = ref(0)
    
    function changeCount(){
        console.log(count)
        //通过.value获取数据
        count.value++
    }

console.log结果: image.png ref()会返回一个RefImpl对象,将我们的数据放在了这个对象的value属性中,上图中可以看到value属性有get 和set方法,ref底层其实是通过Object.defineProperty()来实现响应式的

ref处理复杂数据类型

    import { ref } from 'vue'
    let refObj = ref({
        count:1
    })
    
    function changeRefObjCount(){
        refObj.value.count++
        console.log(refObj)
    }

console.log结果:

截屏2023-07-22 16.37.29.png 从图上可以看到ref()返回的还是一个RefImpl对象,数据依旧被放在了value属性上,value依旧有get和set方法,但是此时的value是一个Proxy对象,ref定义复杂类型响应式数据时,底层其实是通过Object.defineProperty和Proxy一起实现的

ref()实现的响应式是深度的

            let refDeepObj = ref({
                student:{
                    address:{
                        city:'chengdu'
                    }
                }
            })
    
            function changeAddress(){
                refDeepObj.value.student.address.city = 'xian'
                console.log(refDeepObj)
            }
      <div>ref深度响应式:</div>
      <div>{{refDeepObj.student.address.city}}</div>
      <button @click="changeAddress">修改address</button>

修改前:

image.png

修改后:

image.png

在模板中使用ref定义的响应式数据

在模板中使用ref定义的响应式数据时,不需要使用.value去获取,因为在模板中自动帮我们解包了

    // 基本类型
    <div>{{count}}</div>
    // 复杂类型
    <div>{{refObj.count}}</div>

但是模板中自动解包有个前提条件:这个ref响应式数据必须是模板渲染上下文中的顶层属性

      let x = ref(1)
      let y = {z:ref(1)}
      <div> x:{{x+1}}</div>
      <div> y:{{y.z+1}}</div>
      <div> y(.value):{{y.z.value+1}}</div>

最终页面呈现结果:

image.png

x作为顶层属性被自动解包,y.z这个ref响应式对象不是顶层属性,在模板中不会自动解包,需要使用.value去获取数据

但是但是但是,如果y.z作为插值语法中表达式的最终值又会自动解包了

     <div>y.z作为文本插值最终值:{{y.z}}</div>

页面呈现效果:

image.png

这种情况其实是它自动帮你处理了,<div>y.z作为文本插值最终值:{{y.z}}</div>就相当于<div>y.z作为文本插值最终值:{{y.z.value}}</div>

shallowRef

ref实现的响应式是深度的,那如果有一个数据,他层次比较深,但是他里面的属性是不会被修改,要修改只会被整体替换修改,如果使用ref来处理响应式就不是很合适了

shallowRef可以实现浅层响应式,对于普通类型来说,实现的效果和ref没有区别,对于复杂类型来说,它只会去响应第一层的修改

修改非第一层数据:

  let shallowRefObj = shallowRef({
                    a:{
                        b:{
                            c:1
                        }
                    }

                })
                function changeC(){
                    shallowRefObj.value.a.b.c++
                    console.log(shallowRefObj.value.a.b.c)
                }       

截屏2023-07-22 18.35.46.png

数据发生了变化,但是页面没有响应变化

修改第一层数据:

               let shallowRefObj = shallowRef({
                    a:{
                        b:{
                            c:1
                        }
                    }

                })
                function change(){
                    shallowRefObj.value=1
                    console.log(shallowRefObj.value)
                }

修改前:

image.png

修改后:

截屏2023-07-22 18.41.09.png

数据发生变化,页面也响应了这个变化

shallowRef处理复杂数据数据类型,只有在修改.value的时候才会被监测到

reactive

    import { reactive } from 'vue'
    const state = reactive({ count: 0 })

image.png

reactive()返回的是一个Proxy对象,reactive底层是通过Proxy来实现响应式的

Usage in template:

<button @click="state.count++">
  {{ state.count }}
</button>

代理一致性

  1. reactive返回的是原始对象的Proxy,与原始对象不相等
    const originObj = {}
    const originProxy = reactive(originObj)
    console.log(originObj===originProxy) // false
  1. 对同一个原始对象调用reactive()会总是返回同样的代理对象
    console.log(originProxy === reactive(originObj)) // true
  1. 对一个已经存在的代理对象调用reactive()会返回其本身
     console.log(originProxy === reactive(originProxy)) //true

局限性

  1. 只能处理复杂类型数据
    let numberReactive = reactive(0)

直接报错:

image.png

  1. 一个代理对象被整个替换为另一个代理对象不会被监测到
            let state = reactive({ count: 0 })

            function replaceState(){
                state = reactive({count:1})
                console.log(state.count) // 1
            }
      <div>{{state}}</div>
      <button @click="replaceState"> 整个替换reactive代理对象</button>

修改前:

image.png

修改后:

image.png

打印出来的数据是修改以后的数据,对于reactive的代理对象整个替换没办法被监测到

3.解构后丢失响应式

     let state = reactive({ count:1})
     let {count} = state
        <div>{{count}}</div>
        <button @click="count++">修改reactive中解构出的count</button>

修改前&修改后:

image.png

4.代理对象属性作为实参传入,会失去反应式连接

             let state = reactive({count:1})
             function changeCount(count){
                    count++      
                    console.log(state)            
                }
        <div>{{state.count}}</div>
        <button @click="changeCount(state.count)">代理对象属性作为实参传入进行修改</button>

修改前&后:

image.png

打印数据:

image.png

数据也没有发生变化,可以理解为传入的数据和代理对象没有关联

shallowReactive

修改对象非顶层数据

               let shallowReactiveObj = shallowReactive({
                    a:{
                        b:{
                            c:1
                        }
                    }

                })
                function change(){
                    shallowReactiveObj.a.b.c++
                    console.log(shallowReactiveObj.a.b.c)
                }

截屏2023-07-22 18.45.37.png

修改对象顶层数据

              let shallowReactiveObj = shallowReactive({
                    count:0,
                    a:{
                        b:{
                            c:1
                        }
                    }

                })
                function change(){
                    shallowReactiveObj.count++ 
                    console.log(shallowReactiveObj.count)
                }

image.png

ref作为reactive属性值

  1. ref作为reactive对象属性时,会自动展开,行为类似于普通属性
                let a = ref(0)
                let obj = reactive({
                    a,
                })

                function changeA(){
                    // 此处不用写成obj.a.value
                    obj.a++ 
                    console.log(obj.a) //1 
                }
                changeA()
  1. 新的ref赋值给关联了ref的属性,会被替换,原来的ref与reactive对象失去关联
                let a = ref(0)
                let b  = ref(0)
                let obj = reactive({
                    a,
                })

                function changeA(){
                    obj.a=b
                    obj.a++
                    console.log(obj.a) // 1
                    console.log(b.value) // 1 
                    console.log(a.value) // 0
                }
  1. 只有当嵌套在一个深层响应式对象内时,才会自动解包,作为浅层响应式对象,属性被访问时不会被自动解包
                let a = ref(0)
                let b  = ref(0)
                let obj = reactive({
                    a,
                })
                let shallowObj = shallowReactive({
                    b
                })

                function changeA(){
                    obj.a++
                    console.log(obj.a) // 1

                    shallowObj.b.value++
                    console.log(shallowObj.b.value) // 1
                    
                    //不会自动解包
                    shallowObj.b++
                    console.log(shallowObj.b) // NaN
                }

ref出现在数组或集合中

ref作为属性值出现在数组和集合中,使用时不会被自动解包

                let arr = reactive([ref(0)])
                console.log(arr[0]) // RefImpl对象

                let map = reactive(new Map([['count',ref(0)]]))
                console.log(map.get('count'))

image.png