详解vue2和vue3如何定义响应式数据
作者:ashklwi
响应式原理
Vue2和Vue3的响应式实现原理是不同的
Vue2
底层是通过es5
的Object.defineProperty
,使用Object.defineProperty()进行数据劫持,结合订阅发布的方式实现,有一定的局限性。Vue3
底层是通过es6
的Porxy
, 通过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定义响应式数据的资料请关注脚本之家其它相关文章!