详解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 组件传值几种常用方法【总结】

    Vue 组件传值几种常用方法【总结】

    本文是脚本之家小编给大家整理的vue组件传值几种常用方法,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-05-05
  • Vue watch 侦听对象属性详解

    Vue watch 侦听对象属性详解

    Vue的watch侦听器格式有两种:方法格式和对象格式的侦听器,这篇文章主要介绍了Vue watch 侦听对象属性相关知识,需要的朋友可以参考下
    2023-04-04
  • 解决Vue-Router升级导致的Uncaught (in promise)问题

    解决Vue-Router升级导致的Uncaught (in promise)问题

    这篇文章主要介绍了解决Vue-Router升级导致的Uncaught (in promise)问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • vue实现购物车选择功能

    vue实现购物车选择功能

    这篇文章主要为大家详细介绍了vue实现购物车选择功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01
  • 前端vue滚动条滚动监听问题成功解决办法

    前端vue滚动条滚动监听问题成功解决办法

    在Vue中监听滚动事件可以通过使用指令或者自定义事件来实现,这篇文章主要给大家介绍了关于前端vue滚动条滚动监听问题成功解决的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-09-09
  • 基于elementUI实现图片预览组件的示例代码

    基于elementUI实现图片预览组件的示例代码

    这篇文章主要介绍了基于elementUI实现图片预览组件的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • 解决vue运行报错Error:Cannot find module '@vue/cli-plugin-babel'

    解决vue运行报错Error:Cannot find module '@vue/cli-plugin-b

    解决了因为版本问题在创建项目时出现的各种报错问题,这次在运行时出现的问题,下面这篇文章主要给大家介绍了关于解决vue运行报错Error:Cannot find module '@vue/cli-plugin-babel'的相关资料,需要的朋友可以参考下
    2023-04-04
  • vue3中的defineExpose使用示例教程

    vue3中的defineExpose使用示例教程

    这篇文章主要介绍了vue3中的defineExpose使用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • Vue组件二次封装的一些实用技巧总结

    Vue组件二次封装的一些实用技巧总结

    我们在做项目经常会遇到组件功能不能满足业务需求的时候,这时候需要在原有的组件上进行二次封装,下面这篇文章主要给大家介绍了关于Vue组件二次封装的一些实用技巧,需要的朋友可以参考下
    2022-04-04
  • vue路由传参之使用query传参页面刷新数据丢失问题解析

    vue路由传参之使用query传参页面刷新数据丢失问题解析

    这篇文章主要介绍了vue路由传参使用query传参页面刷新数据丢失问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04

最新评论