Vue3 – Ref 与 Reactive 、readonly响应式函数

简介

Vue3 中所提供的响应式方法,不单单使用 set 和  get 方法了,提供了两种创建响应式函数的方式 ref 与 reactive 两种。

 

ref 函数

ref 函数是 reference 的简称,中文名为“引用”的意思,被 ref 包装加工过的基础数据类型,都将变为 RefImpl 对象数据。

// 需要引用 { ref } 暴露
import {ref} from "vue";
export default {
  name: 'App',
  setup() {

    /**
     * 定义 ref 响应式函数
     * @type {Ref<UnwrapRef<string>>}
     */
    let name = ref("张三");
    let age = ref(18);
    let job = ref({
      type: "前端工程师",
      salary: "30k"
    })

    /**
     * 修改 ref 对象中的数据,并实现响应式处理
     */
    function changeData() {
      name.value = "李四";
      age.value = 20;
      // 修改 ref 包装了的对象的方式
      job.value.type = "Java工程师";
      job.value.salary = "40k";
    }

    return {
      // 返回自定义数据
      name, age,
      changeData
    }
  }
}

原理:ref 依然使用 Vue2 上的Object.defineProperty 的方式定义 get set 函数,实现响应式,但对于对象类型的数据,Vue3 使用了 ES6 中的新特性 Proxy 方式。

注意:

1.如果需要在内部方法体内修改 ref 对象中的数据时,需要使用 .value 取得数据。

2.在 template 模板中使用 ref 对象,Vue3 已自动帮我们处理了 .value 访问的问题,所以不再需要手动在 template 中加上 .value 访问数据

3.ref 只使用 Object.defineProperty 的方式包装了基础数据类型,对于使用 ref 包装的对象数据,ref将“求助”Proxy 对对象进行响应式包装,并未使用 Object.defineProperty 的方式包装对象内的数据 。因此,如果要修改 ref 包装了的对象中的成员数据时,只要使用 .value.xxx = xx 就可以。

 

shallowRef 函数

shallowRef 是对于 ref 函数的浅响应式,对于基础数据类型而言,shallowRef 函数与 ref 函数功能是一致的。

但是当 ref 包裹的是一个对象时,ref 会求助于 reactive 对对象创建Proxy实现响应式。

但是 shallowRef 包裹的是一个对象时,shallowRef 并不会求助于 reactive. 而是直接认为该对象就是 Object 类型,相对而言,shallowRef 包裹的对象里面的数据,是没有响应式功能的。但如果当整个对象发生改变时(如对象内存地址发生改变),shallowRef 会作响应式处理。

 

reactive 函数

reactive 函数是 Vue3 中专门用来包装 对象 类型的数据,且仅只支持 对象 类型的数据,使用 ES6 新特性 Proxy 对对象数据进行封装处理。

import {reactive} from "vue"

export default {
  name: 'App',
  setup() {

    /**
     * 定义 ref 响应式函数
     * @type {Ref<UnwrapRef<string>>}
     */
    let name = ref("张三");
    let age = ref(18);

    /**
     * 定义 reactive 响应式函数
     * @type {UnwrapNestedRefs<{type: string, salary: string}>}
     */
    let job = reactive({
      type: "前端工程师",
      salary: "30k"
    });
    // reactive 也支持使用数组
    let hobby = reactive(["a","b","c"])


    /**
     * 修改 ref 对象中的数据,并实现响应式处理
     */
    function changeData() {
      name.value = "李四";
      age.value = 20;
      // 修改 reacitve 包装了的对象的方式
      job.type = "Java工程师";
      job.salary = "40k";

      // 直接通过数组下标修改数组中的成员数据
      hobby[0] = "d";
    }

    return {
      // 返回自定义数据
      name, age,
      changeData
    }
  }
}

注意:reactive 不能包装基础数据类型,包装基础数据类型应使用 ref.而如果包装的是对象类型的数据,应当使用 reactive,而不使用 ref.因为使用reactive包装的数据,在内部方法中修改可以省去使用 .value 的操作。

 

且 reactive 使用了深度 Proxy,即使嵌套结构再深的对象成员数据,依然可以被响应。

 

shallowReactive

shallowReactive 是针对 reactive 的浅响应式处理,当 reactive 对一个对象进行响应式加工时,会对对象中的所有嵌套对象也一并加工成响应式。

但 shallowReactive 仅对对象的第一级成员变量进行响应式处理,对于对象里的嵌套对象,shallowReactive 不会作加工处理,因此如果使用shallowReactive包裹的多层次多象时,其内部的对象数据发生变化时,不会产生响应式处理。

当对象的第一级成员数据发生变化时,才会被响应。

 

解决Vue2中的问题

增删响应式数据问题

对于Vue2中的增删数据而言,不能通过直接增加或删除进行操作,因为这样Vue2不能识别到数据的变动,也不存在 get set 方法,如下

data(){
   return {
      person:{
         name: "张三"
         age:18
       }
  }
},

methods:{
    change(){
        // 增加 person 中的 sex 属性
           this.person.sex = "男"

        // 删除 person 中的 name 属性
           delete this.person.name;
    }
}

以上方法在Vue2 中起不到响应式的作用,因为 Vue2 使用的是 Object.defineProperty() 方法对数据进行监控,但是对于删除和增加,Vue2 并不能通过这种方式进行,这是因为 Object.defineProperty() 不支持 delete 和 增加的方法。

 

需要使用 Vue2 提供的 this.$set(Vue.set) 方法增加数据,和使用 this.$delete(Vue.delete) 方法删除数据,如下

data(){
   return {
      person:{
         name: "张三"
         age:18
       }
  }
},

methods:{
    change(){
        // 增加 person 中的 sex 属性
          this.$set(this.person,'sex','男');

        // 删除 person 中的 name 属性
          this.$delete(this.person,'name');
    }
}

 

 

而Vue3中,则可以直接通过增删数据自动改变

 

数组成员修改问题

对于Vue2 的数据处理而言,也不能通过直接的数组下标修改数据,如下

data(){
   return {
      person:{
         name: "张三"
         age:18,
         hobby:["学习","睡觉"]
       }
  }
},

methods:{
    change(){
        // 修改数组中第一个成员的数据
          this.person.hobby[0] = "吃饭"
    }
}

以上代码在Vue2中并不能起到作用,原因也是 Object.defineProperty() 不支持 数组通过这种方式进行修改。

 

而是通过使用 Vue2 提供的 splice 方法进行操作:

data(){
   return {
      person:{
         name: "张三"
         age:18,
         hobby:["学习","睡觉"]
       }
  }
},

methods:{
    change(){
        // 修改数组中第一个成员的数据
          this.person.hobby.splice(0,1,'吃饭')
    }
}

而Vue3中,则可以直接通过数组下标的方式改变成员数据。

 

ref 与 reactive 的区别

从定义数据的角度对比:

ref 用来定义 : 基本数据类型

reactive 用来定义:对象(或数组)类型的数据

备注:ref 也可以用来定义对象(或数组)类型数据,它内部会自动通过 reactive 转为代理对象

从原理角度对比:

ref 通过 Object.defineProperty() 的 get 和 set 来实现响应式(数据劫持)

reactive 通过使用 Proxy 来实现响应式(数据劫持),并通过 Reflect 操作源对象内部的数据。

从使用角度对比:

ref 定义的数据:操作数据需要 .value, 读取数据时模板中直接读取不需要 .value.

reactive 定义的数据:操作数据与读取数据:均不需要 .value

 

readonly

readonly 故名思义就是,把一个响应式数据作为一个只读的数据,当出现修改该响应式数据时,将被阻止,readonly是深只读,也就是说,响应式数据中的所有成员(无论嵌套多少层)都会被阻止改写。

代码如下:

import {reactive,toRefs,readonly} from "vue"
export default {
  name: 'App',

  setup() {

    let person = reactive({
      name: "张三",
      age: 18,
      job: {
        j1: {
          salary: 20
        }
      }
    });
    
    // 创建只读响应式数据
    person = readonly(person);

    return {
      /**
       * 相当于返回以下别名,但仅分离第一级对象成员
       * 如 job 内的 j1 不会再被分离
       * name: person.name
       * age: person.age
       * job: person.job
       */
      ...toRefs(person)
    }
  }
}

 

 

shallowReadonly

shallowReadonly 是 readonly 的浅只读函数,把一个响应式数据作为一个只读的数据,当出现修改该响应式数据时,将被阻止,shallowReadonly 是浅只读,也就是说,只会阻止响应式数据中的第一级成员数据不被改写,嵌套对象成员数据可以被改写。

 

Vue3 中的响应数据原理

Vue2 中使用 Object.defineProperty() 函数对对象中的每一个成员数据进行单独监控,Vue3中,对于对象而言,Vue3 使用了ES6 全新的特性,Proxy.Proxy 可以直接对整个对象数据中的所有成员进行统一监控,只要对象中有任何变动,都会引起Proxy的触发。其具体基础代码如下:

<script>
    let person = {
        name: "张三",
        age: 18
    }

    let p = new Proxy({
        /**
         * 对 person 进行 Proxy 代理包装,
         * 包装后 person 中的任意数据被访问,都会引起 Proxy get 的触发
         * @param target 源数据,这里指 person
         * @param propName 被变动的属性名
         * @returns {*}
         */
        get(target, propName) {
            console.log(`有人获取${target}对象的${propName}属性`);
            return Reflect.get(target, propName);
        },

        /**
         * 对 person 进行 Proxy 代理包装,
         * 包装后 person 中的任意数据发生变化(包括数据的修改和属性的增加),都会引起 Proxy 的触发
         * @param target 源数据,这里指 person
         * @param propName 被变动的属性名,或增加的属性名
         * @param value 变动后的新数据,或增加的属性的值
         */
        set(target, propName, value) {
            console.log(`有人修改了${target}对象的${propName}属性,其值是${value}`);
            Reflect.set(target, propName, value);
        },

        /**
         * 对 person 进行 Proxy 代理包装,
         * 包装后 person 中的任意数据发生删除时,都会引起 Proxy 的deleteProperty 触发
         * @param target 源数据,这里指 person
         * @param propName 被删除的属性名
         * @returns {boolean} 需要返回一个boolean值,告诉是否删除成功
         */
        deleteProperty(target, propName) {
            console.log(`有人删除了${target}对象的${propName}属性`)
            return Reflect.deleteProperty(target, propName);
        }
    });

</script>

通过使用 Reflect 反射对对象中的属性进行增删改查

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Vue3 – Ref 与 Reactive 、readonly响应式函数
简介 Vue3 中所提供的响应式方法,不单单使用 set 和  get 方法了,提供了两种创建响应式函数的方式 ref 与 reactive 两种。   ref 函数 ref 函数是 reference 的简称,中文名为“引用”的……
<<上一篇
下一篇>>