likes
comments
collection
share

CompositionAPI中的ref,reactive响应式引用的用法和原理

作者站长头像
站长
· 阅读数 53

响应式引用

原理

通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新

ref 处理基础类型的数据

1.普通的变量

例子

const app = Vue.createApp({
    template: `
        <div>{{name}}</div>
    `,
    setup(props, context) {
        let name = 'LeBrown'
        setTimeout(() => {
            name = 'James'
        }, 2000);
        return {
            name

        }
    }
})
const vm = app.mount('#root')

我们发现2秒后name的值并没有从LeBrown变成James,也就是说name并不是响应式的数据,它只是普通的变量

2.将普通的变量变成响应式的引用

CompositionAPI中通过2个语法可以将这种普通的变量变成响应式的引用,响应式的引用的特点是当值发生变化时,页面也会跟着发生变化,是一种双向绑定的数据

例子

const app = Vue.createApp({
    template: `
        <div>{{name}}</div>
    `,
    setup(props, context) {
        let { ref } = Vue;
        // 'LeBrown' 变成 proxy({value: 'LeBrown'})这样的一个响应式引用
        let name = ref('LeBrown')
        setTimeout(() => {
            name.value = 'James'
        }, 2000);
        return {
            name
        }
    }
})
const vm = app.mount('#root')

运行结果

 CompositionAPI中的ref,reactive响应式引用的用法和原理

备注

  • 基础类型的数据调用ref后就会通过proxyLeBrown 变成 proxy({val: 'LeBrown '}) 这样的响应式引用
  • 使用ref将基础类型的数据LeBrown转成一个对象{value:'LeBrown'},因此在设置值的时候就要使用name.value='xxx'
  • vue在做模板处理的时候会自动的做个转换,也就是说如果知道name是被ref返回的响应式引用,会自动的调用name.value,因此再模板中只需要写{{name}}而不需要写name.value
  • proxy没法对基础类型的数据做代理,ref底层做的事情是将基础类型的数据LeBrown转成对象{value:'LeBrown'}的形式,然后再通过proxy做代理,因此ref只适用于处理基础类型的数据,而不能处理引用数据类型的数据

reactive处理非基础类型的数据

1. 让对象上的属性值变成响应式

使用reactive实现上面的例子

例子

const app = Vue.createApp({
    template: `
        <div>{{nameObj.name}}</div>
    `,
    setup(props, context) {
        const { reactive } = Vue;
        // { name: 'LeBrown' } 变成 proxy({ name: 'LeBrown' })这样的一个响应式引用
        let nameObj = reactive({ name: 'LeBrown' })
        setTimeout(() => {
            nameObj.name = 'James'
        }, 2000);
        return {
            nameObj
        }
    }
})
const vm = app.mount('#root')

2. 让数组中的值变成响应式

例子

 const app = Vue.createApp({
    template: `
        <div>{{nameObj[0]}}</div>
    `,
    setup(props, context) {
        const { reactive } = Vue;
        let nameObj = reactive([123])
        setTimeout(() => {
            nameObj[0] = 456
        }, 2000);
        return {
            nameObj
        }
    }
})
const vm = app.mount('#root')

运行结果

 CompositionAPI中的ref,reactive响应式引用的用法和原理

备注

  • ref一样,reactive会将非基础类型的数据{name: 'LeBrown'}变成proxy({name:'LeBrown'})这样的响应式引用,使用方法也和ref类似
  • 用了Composition新的语法refreactive之后就可以替代之前将数据写在data中的写法(也是响应式数据),在vue3中就可以不用写data,可以写成用refreactive的形式定义数据

其他Composition API

1. readonly不希望是响应式而是只读的

也就是说现在不希望对nameObj中属性的值进行变更,而是希望它变成只读的

例子

 const app = Vue.createApp({
    template: `
        <div>
            {{nameObj[0]}}
        </div>
    `,
    setup(props, context) {
        const { reactive,readonly } = Vue;
        let nameObj = reactive([123])
        const copyNameObj = readonly(nameObj)
        setTimeout(() => {
            nameObj[0] = 456
            copyNameObj[0] = 456
        }, 2000);
        return {
            nameObj,
            copyNameObj
        }
    }
})
const vm = app.mount('#root')

运行结果

 CompositionAPI中的ref,reactive响应式引用的用法和原理

也就是说,在响应式引用前加上readonly,那么这个这个响应式引用就变成只读的了

2. toRefs将解构时的基础类型值转成响应式

如果在模板中我不想写{{nameObj.name}},而是希望能把namenameObj中解构出来,同时name也要是响应式引用

例子

const app = Vue.createApp({
    template: `
        <div>{{name}}</div>
    `,
    setup(props, context) {
        const { reactive } = Vue;
        let nameObj = reactive({ name: 'LeBrown' })
        setTimeout(() => {
            nameObj.name = 'James'
        }, 2000);
        const {name} = nameObj
        return {
            name
        }
    }
})
const vm = app.mount('#root')

但是这样写2秒之后,页面上的内容并没有发生变化,为什么呢?

因为nameObj它是经过reactive处理过的,它是响应式的引用

但是经过解构赋值之后,它就只是一个基础类型的数据,因此它不具备响应式的特点

Vue中提供了新的响应式的语法toRefs

例子

const app = Vue.createApp({
    template: `
        <div>{{name}}</div>
    `,
    setup(props, context) {
        const { reactive,toRefs } = Vue;
        let nameObj = reactive({ name: 'LeBrown' })
        setTimeout(() => {
            nameObj.name = 'James'
        }, 2000);
        const {name} = toRefs(nameObj)
        return {
            name
        }
    }
})
const vm = app.mount('#root')

备注

toRefs原理: proxy({name: 'LeBrown'}) ===> 转成 {name: proxy({value: 'LeBrown'})}

这样调用 const {name} = toRefs(nameObj)实际上调用的是proxy({value: 'LeBrown'}

而模板中中调用{{name}},实际上是调用了{{name.value}}

3. toRef

例子

1. 引入的需求

const app = Vue.createApp({
    template: `
        <div>{{name}}</div>
    `,
    setup(props, context) {
        const { reactive,toRefs } = Vue;
        const  data = reactive({ name: 'LeBrown' })
        // 解构出来  toRefs将proxy({value: 'LeBrown'}) 变成 {name: proxy({{value: 'LeBrown'}})}
        const {name} = toRefs(data)
        setTimeout(() => {
            name.value = 'James'
        }, 2000);
        return {name}
    }
    })
const vm = app.mount('#root')

如果将解构的时候解构的不是name而是age会发生什么?

const app = Vue.createApp({
    template: `
        <div>{{age}}</div>
    `,
    setup(props, context) {
        const { reactive,toRefs } = Vue;
        const  data = reactive({ name: 'LeBrown' })
        // 解构出来  toRefs将proxy({value: 'LeBrown'}) 变成 {name: proxy({{value: 'LeBrown'}})}
        const {age} = toRefs(data)
        setTimeout(() => {
            age.value = 'James'
        }, 2000);
        return {age}
    }
})
const vm = app.mount('#root')

data中并没有age,把age结构出来,会生效吗,如果生效页面会不会先先是空白,再变成James

实际结果却报错了

 CompositionAPI中的ref,reactive响应式引用的用法和原理

备注

为什么要有toRef?

toRefs从响应式对象data中找数据,如果找不到它不会给默认的响应式的引用,而是给age一个undefined,这样由于{age}不存在于响应式对象data中,那么它就不具备响应式.而toRef就是为了解决这样的问题而产生的

2. 使用toRef改写

const app = Vue.createApp({
    template: `
        <div>{{age}}</div>
    `,
    setup(props, context) {
        const { reactive,toRef } = Vue;
        const  data = reactive({ name: 'LeBrown' })
        const age = toRef(data,"age")
        setTimeout(() => {
            age.value = 'James'
        }, 2000);
        return {age}
    }
})
const vm = app.mount('#root')

运行结果

 CompositionAPI中的ref,reactive响应式引用的用法和原理

备注

当尝试从响应式对象data中取age,如果能取到就取,如果取不到就给个默认的空的响应式数据,它依然具备响应式的特性。

它主要为了应对从响应式对象中取值的时候会出现这个对象中没有这个属性,而又不希望报错,一直具备响应式特征的时候就使用toRef而不是使用toRefs

转载自:https://juejin.cn/post/7245171528123564090
评论
请登录