【Vue3】数据的返回和响应式处理(ref & reactive)

发布于:2025-05-26 ⋅ 阅读:(644) ⋅ 点赞:(0)

目录

一、拉开序幕的setup

二、ref函数

2.1 访问对象的响应式处理

小结:ref函数

三、reactive函数 

3.1 reactive同样也可以修改数组:

3.2 reactive小结: 

四、Vue3中的响应式原理

4.1 vue2的响应式,对象属性的添加

4.2 同样删除也是如此:

4.3 vue3的响应式

4.4 模拟vue2中实现响应式

4.5 小插曲:对于代码折叠

4.6 vue3中的Proxy关于person代理的操作十分方便!

4.6.1 Proxy利用Reflect进行数据返回会更加安全:

五、reactive对比ref

总结不易~本章节对我有很大的收获,希望对你也是!!!


一、拉开序幕的setup

  1. 理解:Vue3.0中一个新的配置项,值为一个函数。

  2. setup是所有Composition API(组合API)“ 表演的舞台 ”</i>。

  3. 组件中所用到的:数据、方法等等,均要配置在setup中。

  4. setup函数的两种返回值:

    1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)

    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)

  5. 注意点:

    1. 尽量不要与Vue2.x配置混用

      • Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。

      • 但在setup中不能访问到Vue2.x配置(data、methos、computed...)。

      • 如果有重名, setup优先。

    2. setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)

再setup函数内直接写数据和方法也就是vue2里面的data()数据 和 methods方法然后一股脑的全部进行返回,唯一区别就是setup函数内部不在需要用this进行指向,因为都在同一作用域内,就可以直接再模板内进行引用 {{  }}

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <button @click="sayHello">说话</button>
</template>

<script>

export default {
  name: 'App',
  // 此处只是测试一下 暂时不考虑响应式!
  setup() {
     // 数据就直接写
     let name = '张三'
     let age = 18

     // 方法 setup函数里面包函数 闭包函数 都在同一个作用域,不需要this
     function sayHello() {
       //  alert(`我叫${this.name},我碎掉了,你好啊!!`)
       alert(`我叫${name},我碎掉了${age}岁,你好啊!!`)
     }

    // 返回一个对象 (常用)
     return {
      name,
      age,
      sayHello
     }
  }
}
</script>

这里不掩饰了,vue3可以用vue2的方式来进行书写,但是不建议3 和 2的模板在同一个js内出现,vue2可以访问vue3里面的各种信息,通过this调用,但是这里vue3想通过this来访问vue2模板里面的信息就会出现undefined的信息

本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/03_src3_ref%E5%87%BD%E6%95%B0

二、ref函数

之前说vue2的ref属性就像是id的替代者,我们给input打个标识,好获得焦点属性;但是vue3中多了一个ref函数,用来数据的响应式

还是按照我们正常的方式来进行点击事件调用方法,我们想要改一个人的信息,并且再控制台打印出这个人的信息是否又被修改!

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{ name }}</h2>
  <h2>年龄:{{ age }}</h2>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>

export default {
  name: 'App',
  setup() {
     // 数据就直接写
     let name = '张三'
     let age = 18

     // 方法
     function changeInfo() {
      name = '李四'
      age = 48
      console.log(name, age)
     }

     return {
      name,
      age,
      changeInfo
     }
  }
}
</script>

可以看到,点击之后数据被修改,但是vue并没有渲染到页面上!

就是因为在这里定义的数据只是一个普通的字符串数据,不是响应式的,vue根本不会监测到,那就要从vue里面引入一个ref函数来形成一个响应式数据!

此时的响应式数据name, age成为了一个多了很多属性的对象,里面value属性值是专门用来存放该属性的

<script>
import {ref} from 'vue'
export default {
  name: 'App',
  setup() {
     // 数据就直接写
     let name = ref('张三')
     let age = ref(18)

     // 方法
     function changeInfo() {
      console.log(name, age)
     }

     return {
      name,
      age,
      changeInfo
     }
  }
}
</script>

所以响应式的修改就是要访问ref的value值:

import {ref} from 'vue'
export default {
  name: 'App',
  setup() {
     // 数据就直接写
     let name = ref('张三')
     let age = ref(18)

     // 方法
     function changeInfo() {
      name.value = '李四'
      age.value = '20'
      console.log(name, age)
     }

     return {
      name,
      age,
      changeInfo
     }
  }
}

当前的value值就是通过get 和 set来实现的! 在模板引用的时候,不需要.value ,vue3会自动监测到帮我们进行.value操作

2.1 访问对象的响应式处理

也是同样如此,先访问value属性,然后value属性里面就自然包含了job的一系列属性名

     let name = ref('张三')
     let age = ref(18)
     let job = ref({
      type: '前端工程师',
      salary: '30k'
     })

     // 方法
     function changeInfo() {
      name.value = '李四'
      age.value = '20'
      job.value.type = 'c++工程师'
      job.value.salary = '100k'
      // console.log(name, age)
     }

 这里呢就有一个小细节,ref是把对象进行数据劫持,使用了Proxy方法,可以看我前面的一篇文章,对 对象先进行修改后在返回!:【JavaScript】用 Proxy 拦截对象属性-CSDN博客文章浏览阅读937次,点赞20次,收藏26次。你可以把Reflect理解为一个官方提供的工具库,专门用于安全、标准地操作对象。// 普通写法// 更安全写法是“官方提供的设置方法”,多了一层保护,比如你设置失败它会给你 false,不会直接报错。},})只需要将所有的target[key] = value 修改成Reflect.set(target, key, value)即可,函数会自动帮我们生成对应的方法的t=P1C7t=P1C7。 https://blog.csdn.net/2301_80636070/article/details/148059632?spm=1001.2014.3001.5501

这里的Proxy是由内部的reactive函数进行了封装而成!

小结:ref函数

  • 作用: 定义一个响应式的数据

  • 语法: const xxx = ref(initValue)

    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。

    • JS中操作数据: xxx.value

    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>

  • 备注:

    • 接收的数据可以是:基本类型、也可以是对象类型。

    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。

    • 对象类型的数据:内部 “ 求助 ”了Vue3.0中的一个新函数—— reactive函数

本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/04_src3_reactive%E5%87%BD%E6%95%B0

三、reactive函数 

从vue引入reactive方法~

     import {ref, reactive} from 'vue'
     let job = reactive({
      type: '前端工程师',
      salary: '30k'
     })

     // 方法
     function changeInfo() {
       job.type = 'c++工程师'
       job.salary = '100k'
      console.log(job)
     }

通过reactive来响应式对象,就不需要再通过value取对象属性! 

3.1 reactive同样也可以修改数组:

由于vue3响应式reactive修改是封装了Proxy,那么对数组的下标访问就不再是像vue2一样vue会监测不到,由于Proxy是对对象的监视,所以这里对数组下标的修改vue是能够监视到的! (vue2只能对数组进行push,pop等一系列改变数组长度的参数才能让vue监测到!)

     import {ref, reactive} from 'vue'

     let hobby = reactive(['抽咽', '喝酒', '烫头'])

     // 方法
     function changeInfo() {
       hobby[0] = '学习'
     }

由于ref每次都要给响应式数据加value,所以还是推荐用reactive,但是,凡是都有但是,reactive却处理不了常用数据类型,所以我们一般就将所有的常用对象放入person内,然后再用reactive来响应式访问person变成一个响应式对象即可!!!就可以直接进行访问!

<template>
  <h1>一个人的信息</h1>
  <h2>姓名:{{ person.name }}</h2>
  <h2>年龄:{{ person.age }}</h2>
  <h3>工作种类:{{ person.job.type }}</h3>
  <h3>工作薪水:{{ person.job.salary }}</h3>
  <h3>三大爱好是{{ person.hobby }}</h3>
  <button @click="changeInfo">修改人的信息</button>
</template>

<script>
import {ref, reactive} from 'vue'
export default {
  name: 'App',
  setup() {
     // 数据就直接写
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        type: '前端工程师',
        salary: '30k'
      },
      hobby: ['抽咽', '喝酒', '烫头']
    })

     // 方法
     function changeInfo() {
      person.name = '李四'
      person.age = '20'
      person.job.type = 'c++工程师'
      person.job.salary = '100k'
      person.hobby[0] = '学习'
     }

     return {
      person,
      changeInfo
     }
  }
}
</script>

3.2 reactive小结: 

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)

  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)

  • reactive定义的响应式数据是“深层次的”。

  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

本章节素材已上传至Gitee:yihaohhh/我爱Vue - Gitee.comhttps://gitee.com/liu-yihao-hhh/i-love---vue/tree/master/05_Vue3%E4%B8%AD%E7%9A%84%E5%93%8D%E5%BA%94%E5%BC%8F%E5%8E%9F%E7%90%86

四、Vue3中的响应式原理

4.1 vue2的响应式,对象属性的添加

点击后并没有再页面上渲染,就是因为vue并没监测到你对person对象的改变,也就是vue没有做到数据劫持!

    addSex() {
      console.log(this.person.sex)
      this.person.sex = '女'
      console.log(this.person.sex)
    },

要用到另外一直方法:才能让vue知道,你要对对象进行属性添加

import Vue from 'vue'

    addSex() {
      // this.$set(this.person, 'sex', '女')
      Vue.set(this.person, 'sex', '女')
    },

4.2 同样删除也是如此:

删除name,删除成功,但是vue并没有监测到!

    deleteName() {
      console.log(this.person.name)
      delete this.person.name
      console.log(this.person.name)
    }

同样要采用this.$delete()

    deleteName() {
      this.$delete(this.person, 'name')
    }

同样还有对于数组下标直接修改内容,也会存在当前问题!

4.3 vue3的响应式

上面可以看到vue2存在的问题和解决办法都是通过this.$set()来修改对象和数组,$get()来获取数据,vue3就通过reactive封装的Proxy方法就能够很方便的直接来拦截对象中的任意属性变化直接进行数据操作!

当在vue3中直接进行性别添加 和 name删除的时候就可以直接操作!就是因为有了reactive才能够如此的顺利进行响应式操作~

    function addSex() {
      person.sex = '男'
    }

    function deleteName() {
      delete person.name
    }

4.4 模拟vue2中实现响应式

代码实在是过于冗长,对于属性的新增和删除还麻烦

    let person = {
      name: '张三',
      age: 18
    }

    // 模拟Vue2中实现的响应式
    let p = {}
    Object.defineProperty(p, 'name', {
      // delete允许删除
      configurable: true,
      get() { // 有人读取name时调用
        return person.name
      },
      set(value) { // 有人修改name时调用
        console.log('有人修改了name属性,我发现了, 我要去更新界面!')
        person.name = value
      }
    })

    Object.defineProperty(p, 'age', {
      get() { // 有人读取age时调用
        return person.age
      },
      set(value) { // 有人修改name时调用
        console.log('有人修改了age属性,我发现了, 我要去更新界面!')
        person.age = value
      }
    })

4.5 小插曲:对于代码折叠

添加上#region 和 #endregion 不管怎么敲回车这些代码都不会在被展开了

4.6 vue3中的Proxy关于person代理的操作十分方便!

    // 模拟Vue3中的响应式
    const p = new Proxy(person, {})

通过Proxy捕获到数据的变化!

    let person = {
      name: '张三',
      age: 18
    }

    // 模拟Vue3中的响应式
    const p = new Proxy(person, {
      // 有人读取p的某个属性时调用
      get(target, propName) {
        console.log(`有人读取了p身上的${propName}属性`)
        return target[propName]
      },
      // 有人修改或者增加p的某个属性时调用
      set(target, propName, newValue) {
        console.log(`有人修改了p身上的${propName}属性, 我要去更新界面了`)
        target[propName] = newValue
        return true
      },
      // 有人删除p的某个属性时调用
      deleteProperty(target, propName) {
        console.log(`有人删除了p身上的${propName}属性, 我要去更新界面了`)
        return delete target[propName]
      }
    })

4.6.1 Proxy利用Reflect进行数据返回会更加安全:

    // 模拟Vue3中的响应式
    const p = new Proxy(person, {
      // 有人读取p的某个属性时调用
      get(target, propName) {
        console.log(`有人读取了p身上的${propName}属性`)
        // return target[propName]
        return Reflect.get(target, propName)
      },
      // 有人修改或者增加p的某个属性时调用
      set(target, propName, newValue) {
        console.log(`有人修改了p身上的${propName}属性, 我要去更新界面了`)
        // target[propName] = newValue
        Reflect.set(target, propName, newValue)
        return true
      },
      // 有人删除p的某个属性时调用
      deleteProperty(target, propName) {
        console.log(`有人删除了p身上的${propName}属性, 我要去更新界面了`)
        // return delete target[propName]
        return Reflect.defineProperty(target, propName)
      }
    })
  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。

  • 通过Reflect(反射): 对源对象的属性进行操作。

五、reactive对比ref

  • 从定义数据角度对比:

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

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

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

  • 从原理角度对比:

    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。

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

  • 从使用角度对比:

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

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

最后对于我们再写程序的时候,不止一个对象,但是又想再一个reactive里面进行返回,那么我们就可以写到一个data对象内,一起进行返回即可!就又有点vue2的味道了

    let data = reactive({
      person:{},
      animal: {}
    })

总结不易~本章节对我有很大的收获,希望对你也是!!!