# ref

vue3中一般用ref将一个值为基本数据类型的变量变成响应式,它是一个函数,组合式API之一,参数为变量的初始值。举个栗子,比如我们需要一个响应式的变量num,初始值为1。在Vue2中我们会这么做

data() {
    return {
        num: 1
    }
}

把num写在data里。而在vue3中,我们通过setup函数里使用ref定义num,此时num成了一个类型为Ref(reference)的,包含响应式数据的引用对象

setup() {
    const num = ref(1)
}

现在,如果想在js中改变num的值,需要通过num.value。在ref内部,是通过给value属性添加getter/setter来实现对数据的劫持,这点在我们手写shallowRef时需要用上

注意,在<template>内不需要通过.value而是直接使用num

# ref对象

使用ref创建对象,里面任意深度的属性与视图都是响应式的,如在vue2中创建的data

data() {
    return {
        a: {
            b: 1
        }
    }
}

使用ref则为

const data = ref({
    a: {
        b: 1
    }
})

当修改b属性的值时,视图会更新

# shallowRef

只处理value的响应式,不进行对象的reactive处理,也就是说如果传给shallowRef一个对象,这个对象的任何一层属性都不是响应式的

# 手写实现(拦截对数据的操作)

将target赋值给_value属性,通过对象本身的set和get方法来实现对target的操作的拦截

function shallowRef(target) {
    return {
        _value: target,
        get value() {
            console.log('兰街到查询');
            return this._value;
        },
        set value() {
            consoeo.log('拦截到修改');
            this._value = val;
        }
    }
}

可以测试下效果

const testShallowRef = shallowRef('美好的')

打印查看 testShallowRef 对象本身 console.log(testShallowRef):

shallowRef

可以看到有个value属性,值为传递给shallowRef的参数

对testShallowRef进行查改操作

testShallowRef.value += '周五'

testShallowRef

说明确实拦截到了对 testShallowRef 的查询和修改操作,那么就可以继续做一些更新渲染页面的功能。

# shallowRef --对象

与ref不同,shallowRef修改深层属性时,并不会更新视图

<template>
    <div>
        <p>{{data.foo}}</p>
        <button @click="update">update</button>
        <button @click="log">log</button>
    </div>
</template>
<script>
    setup() {
        const data = shallowRef({foo: '1'});
        function update() {
            data.value.foo = '2';
        }
        function log() {
            console.log(data.value.foo)
        }
        return {
            data,
        }
    }
</script>

上面点击update,视图并不会更新,但是点击log按钮时,打印出foo的值为2

想要更新视图,必须给value赋值,直接替换这个对象

function update() {
    data.value = { foo: 200 }
}

再次点击update按钮,视图更新。

shallow即浅的意思,shallowRef只有整个数据变更时才刷新视图

或者在修改数据之后,调用triggerRef方法,主动触发视图刷新

function update() {
    data.value.foo = '300';
    triggerRef(data); // 触发视图刷新
}

需要注意如果组件中有ref/reactive的数据更新引起组件更新,会把shallowRef更新后的数据更新到视图

# 为什么使用shallowRef

因为ref方法会递归遍历对象的所有属性,使所有属性都具备响应性,所以,当对象很复杂且庞大时,过多的监听会导致性能上的损耗。如假设有一个文章列表数组:

像这种做展示用的数据,并不需要每个属性都做响应性,此时使用shallowRef就很合适。

其实可以放在外面不做响应式或者Object.freeze()

# shallReactive

只处理对象最外面一层的响应时数据(浅响应时)

const obj = shallowReactive({name: 'Mr.long', son: {name: 'Mr.liu'}})
// vue不会做son.name响应式

ref与shallowRef区别 (opens new window)

shallowReactive和shallowRef (opens new window)

vue3.0中shallowRef和shallReactive (opens new window)