详解vue2和vue3如何定义响应式数据

 更新时间:2023年11月09日 14:33:59   作者:ashklwi  
这篇文章主要是来和大家一起讨论一下vue2和vue3是如何定义响应式数据的,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解下

响应式原理

Vue2和Vue3的响应式实现原理是不同的

  • Vue2底层是通过es5Object.defineProperty,使用Object.defineProperty()进行数据劫持,结合订阅发布的方式实现,有一定的局限性。
  • Vue3底层是通过es6Porxy, 通过Proxy代理,使用ref或者reactive将数据转化为响应式数据,能够更好地支持动态添加属性和删除属性。它解决了Vue2底层实现的缺点,对数组、层级比较深的对象处理都很优秀,但缺点是浏览器兼容不是很好。

Vue2的响应式数据

在Vue2中,是使用选项式API的方式来编写代码,比如data()、computed()、watch等方法实现响应式

Vue3的响应式数据

reactive和ref

reactive

reactive定义引用数据类型(以对象和数组举例),它能够将复杂数据类型的内部属性或者数据项声明为响应式数据,所以reactive的响应式是深层次的,其底层是通过ES6的Proxy来实现数据响应式,相对于Vue2的Object.defineProperty,具有能监听增删操作,能监听对象属性的变化等优点

  • reactive是一个函数,它可以定义一个复杂数据类型,成为响应式数据;
  • 通常用来定义响应式的对象数据。
<template>
  <div>name:{{obj.name}}</div>
  <button @click="updateName">修改name</button>
</template>
<script>
import { reactive } from 'vue'
export default {
    setup() {
        // 定义响应式对象
        const obj = reactive({
            name: 'lisi',
            age:20
        })
        const updateName = () => {
            obj.name = '我是修改后的name'
            console.log('我是按钮..........');
        };
        return {
            obj,
            updateName
        }
    }
}
</script>

ref函数

ref函数,常用于简单数据类型定义为响应式数据,其实也可以定义复杂数据类型的响应式数据,对于数据未知的情况下ref是最适用的。

在修改值,获取值的时候,需要.value。在模板中使用ref申明的响应式数据,可以省略.value,在js代码中修改ref声明的数据,需要加上.value。

<template>
  <div>name:{{name}}</div>
  <button @click="changeName">修改name</button>
</template>

<script>
import { ref } from 'vue'
export default {
    setup() {
        const name = ref('zhangsan');
        const changeName = () => {
            name.value = 'lisi'
        }
        return {
            name,
            changeName
        }
    }
} 
</script>

两者的不同

  • ref用于定义基本类型和引用类型,reactive仅用于定义引用类型
  • reactive只能用于定义引用数据类型的原因在于内部是通过ES6的Proxy实现响应式的,而Proxy不适用于基本数据类型
  • ref定义对象时,底层会通过reactive转换成具有深层次的响应式对象,所以ref本质上是reactive的再封装(会判断数据的类型进行不同处理)
  • 在脚本里使用ref定义的数据时,记得加.value后缀
  • 在定义数组时,建议使用ref,从而可避免reactive定义时值修改导致的响应式丢失问题
const tableData = reactive([]) // 定义
const getTableData = async () => {
	const { data } = await getTableDataApi() // 模拟接口获取表格数据
	tableData = data // 修改,错误示例,这样赋值会使tableData失去响应式
}

// 方法一:改为 ref 定义
const tableData = ref([])
const getTableData = async () => {
	const { data } = await getTableDataApi()
	tableData.value = data // 使用.value重新赋值
}
// 方法二:使用 push 方法
const tableData = reactive([])
const getTableData = async () => {
	const { data } = await getTableDataApi()
	tableData.push(...data) // 先使用...将data解构,再使用push方法
}
// 方法三:定义时数组外层嵌套一个对象
const tableData = reactive({ list:[] })
const getTableData = async () => {
	const { data } = await getTableDataApi()
	tableData.list = data // 通过访问list属性重新赋值
}
// 方法四:赋值前再包一层 reactive
const tableData = reactive([])
const getTableData = async () => {
	const { data } = await getTableDataApi()
	tableData = reactive(data) // 赋值前再包一层reactive
}

为什么需要两个

虽然ref函数既可以处理基本数据类型也可以处理引用数据类型,但是在普通js代码里修改该响应式数据的值时需要使用.value的写法,会存在.value的嵌套问题,因此使用reactive来处理引用数据类型,避免该问题。

toRef和toRefs

ref是对元数据的拷贝,修改响应式数据时不会影响之前的数据,视图会更新。tooRef和toRefs是对元数据的引用,修改响应式数据时,原数据也会改变,但是视图不会更新,只有原始数据改变后,该数据和视图都会更新。toRef修改的是对象的某个属性,toRefs修改的是整个对象

toRef

toRef 函数的作用:转换响应式对象中某个属性为单独响应式数据,并且转换后的值和之前是关联的(ref 函数也可以转换,但值非关联)。

<template>
  <div class="container">
      <h2>name: {{ obj.name }} age: {{obj.age}}</h2>
      <button @click="updateName">修改数据</button>
  </div></template><script>
  import { reactive } from 'vue'
  export default {
      name: 'App',
      setup() {
          const obj = reactive({
              name: '初映',
              age: 18,
              address: '江西',
              sex: '男',
          })
          const updateName = () => {
              obj.name = '初映CY的前说'
          }
          return { obj, updateName }
      },
  }</script>

这样写有几个问题:

  • 模板中都要使用 obj. 进行获取数据,较为麻烦
  • 明明模板中只用到了 name 和 age,却把整个 obj 进行了导出,没必要,性能浪费。

使用toRef进行修改,只需要将需要的属性return出去即好,且模板中也不需要加obj.前缀了。

<template>
  <div class="container">
      <h2>name: {{ name }} </h2>
      <button @click="updateName">修改数据</button>
  </div></template><script>
  import { reactive,toRef  } from 'vue'
  export default {
      name: 'App',
      setup() {
          const obj = reactive({
              name: '初映',
              age: 18,
              address: '江西',
              sex: '男',
          })
          const name = toRef(obj, 'name')
          const updateName = () => {
              obj.name = '初映CY的前说'
          }
          return { name, updateName }
      },
  }</script>

toRefs

toRefs 函数的作用:转换响应式对象中所有属性为单独响应式数据,并且转换后的值和之前是关联的。

<template>
<div>{{name}}</div>
<div>{{age}}</div>
<button @click="update">修改name</button>
</template>

<script>
import {reactive, toRefs} from 'vue'

export default {
    setup() {
        const obj = reactive({
            name: '张三',
            age:18
        })
        console.log(obj);

        const obj2 = toRefs(obj);
        console.log(obj2); //发现obj2里面的name和age都是响应式属性,指向obj的属性

        // 解构之后重新赋值的是普通对象
        const obj3 = { ...obj };
        console.log(obj3);

        const update =()=> {
            obj.name = '我是修改的原始数据的obj'
        }
        return {
            // 解构obj2,用的时候直接拿属性名,不需要obj2.name或obj2.age
            ...obj2,
            update
        }
    }
}
</script>

修改原始数据obj后会发现,转换过后的obj2中的值会跟着改变。

以上就是详解vue2和vue3如何定义响应式数据的详细内容,更多关于vue定义响应式数据的资料请关注脚本之家其它相关文章!

相关文章

  • vue 使用moment获取当前时间及一月前的时间

    vue 使用moment获取当前时间及一月前的时间

    开发中会有要获取当前日期的需求,有的是获取到当前月份,本文就介绍了vue获取当前日期时间(moment、new Date()),文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2023-08-08
  • vue动画—通过钩子函数实现半场动画操作

    vue动画—通过钩子函数实现半场动画操作

    这篇文章主要介绍了vue动画—通过钩子函数实现半场动画操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • Vue3中watch的用法与最佳实践指南

    Vue3中watch的用法与最佳实践指南

    这篇文章主要给大家介绍了关于Vue3中watch用法与最佳实践的相关资料,watch的作用可以监控一个值的变换,并调用因为变化需要执行的方法,可以通过watch动态改变关联的状态,需要的朋友可以参考下
    2021-07-07
  • 一文详解Vue中加上key后发生什么

    一文详解Vue中加上key后发生什么

    本文主要介绍了一文详解Vue中加上key后发生什么,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • VUE使用ElementUI下拉框 @change事件数据不回显问题

    VUE使用ElementUI下拉框 @change事件数据不回显问题

    这篇文章主要介绍了VUE使用ElementUI下拉框 @change事件数据不回显问题。具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • DataV 全屏容器组件源码解析

    DataV 全屏容器组件源码解析

    这篇文章主要为大家介绍了DataV 全屏容器组件源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Vue出现did you register the component correctly?解决方案

    Vue出现did you register the component 

    这篇文章主要介绍了Vue出现did you register the component correctly?解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • Vue中watch监听属性新旧值相同的问题解决方案

    Vue中watch监听属性新旧值相同的问题解决方案

    这篇文章主要给大家分享了Vue中watch监听属性新旧值相同问题解决方案,如果有遇到相同问题的朋友,可以参考阅读本文
    2023-08-08
  • 解决Vue input输入框卡死的问题

    解决Vue input输入框卡死的问题

    这篇文章主要介绍了解决Vue input输入框卡死的问题,文中同时给大家提到了Vue-element中el-input输入卡顿问题及解决方法,本文结合示例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-09-09
  • 基于Vuejs和Element的注册插件的编写方法

    基于Vuejs和Element的注册插件的编写方法

    这篇文章主要介绍了基于Vuejs和Element的注册插件的编写方法,需要的朋友可以参考下
    2017-07-07

最新评论