Vue3中的数据响应【4】
目录
- 1.vue3中的数据响应:
- 1.概述:
- 2.使用:
- 1.基本类型的数据:ref
- 概述:
- ref语法:
- 案例演示:
- 注意事项:
- 2.对象类型数据:包括对象,数据,及嵌套对象
- 概述:
- reative语法:reative不能用在基本类型数据上
- 示例:通过reative实现转变
- 示例:通过ref实现对象类型数据的转变
- 3.vue3中的数据响应失效:
- 概述:
- 案例:
- 解决方案:
- 方式1:通过ref创建对象类型数据
- 方式2;通过Object的assign方法:
- 4.toRef和toRefs函数:
- 1.概述:
- 2.案例:
1.vue3中的数据响应:
1.概述:
在vue3中,数据默认都是非响应式的(vue2默认为响应式),即修改数据后默认不会同步渲染到页面上;那么要如何将非响应式数据变成响应式呢,实际上在vue3中有两个函数ref和reative,可将默认的非响应式数据变成响应式;
2.使用:
1.基本类型的数据:ref
概述:
在vue3中可以通过ref将基本类型的数据变成响应式;
ref语法:
let 变量名=ref('初始值')
案例演示:
我们还是以上面的案例来实现数据的响应式转变;
<template><h2>名称:{{ name }}</h2><h2 v-text="'地址:'+address"></h2><button @click="changeName">更新名称</button>
</template><!--setup 语法糖-->
<script lang="ts" setup name="School">import {ref} from "vue";let name =ref("猿究院") ;
let address = ref("北大街");
const changeName = () => {console.log("更新name属性:", name);name.value += "a";
}
</script><style scoped></style>
在此案例中,我们通过ref将数据name变成了响应式,然后我们在浏览器页面看一下是否实现了数据的同步渲染;
通过测试结果可以看出通过ref实现了数据的响应式转变;
我们再看一个案例:
<template><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><button @click="changeName">点击修改姓名</button><button @click="changeAge">点击修改年龄</button><button @click="showTel">点击展示联系方式</button>
</template><script lang="ts" setup name="Person">import {ref} from "vue";let name=ref('张三');let age=ref(25);let tel='122xxxxxxxx';const changeName=()=>{name.value+="a";}const changeAge=()=>{age.value++;}const showTel=()=>{alert("联系方式为"+tel);}
</script><style scoped></style>
此案例中我们还是通过ref将数据转变成响应式,再浏览器页面上依然可用看到数据的同步渲染
注意事项:
我们通过ref将基本类型数据转成响应式,不是说将数据本身转成了响应式;实际上通过ref函数得到的返回值为refimpl的实例对象,此对象有个value属性,此属性为响应式的,并且此属性指向我们转变的数据变量。其实我们通过打印转变后的数据也可以看出:因此修改数据时需要通过数据变量.value进行修改
2.对象类型数据:包括对象,数据,及嵌套对象
概述:
在vue3中我们通过reative或ref将对象类型数据转变成响应式数据;
reative语法:reative不能用在基本类型数据上
let 对象名=reative({属性1:值属性2:值
})
示例:通过reative实现转变
<template><h2>汽车:{{Car.brand}}</h2><h2>价值:{{Car.price}}</h2><h2>游戏:</h2><ul><li v-for="item in games " :key="item.id">{{item.game}}</li></ul><h2>嵌套对象:{{obj.a.b.c.d}}</h2><button @click="changeCarBrand">点击修改汽车品牌</button><button @click="changeCarPrice">点击修改汽车价格</button><button @click="changeFirstGame">点击修改第一个游戏</button><button @click="changeObjValue">点击修改嵌套对象深层的属性值</button>
</template><script lang="ts" setup name="Car">
import {reactive, ref} from "vue";//对象数据let Car=reactive({brand:'奥迪',price:400000,})
//对象数组数据:let games=reactive([{id:'1001',game:'金铲铲',},{id:'1002',game:'云顶之弈',},{id:'1003',game:'英雄联盟',},])//嵌套对象数据let obj=reactive({a:{b:{c:{d:9999}}}})//回调函数const changeCarBrand=()=>{Car.brand='奔驰';}const changeCarPrice=()=>{Car.price+=10000;}const changeFirstGame=()=>{games[0].game='三星五费';
}const changeObjValue=()=>{obj.a.b.c.d+=100;
}
</script><style scoped></style>
上述案例中,我们通过reative将对象类型的数据中的属性转成了响应式,包括对象类型数据的属性,数组类型数据的元素以及嵌套对象深层的属性,都可以将其转成响应式;但与ref不同的是,reative函数的返回值为proxy实例对象,此对象就是响应式的,并且此对象会代理指向原始需要转变响应式的对象,因此通过reative对对象类型数据进行修改时可以直接修改,而不需要像ref一样通过数据变量.value进行修改;
测试结果展示:
1.修改对象属性:
2.修改数组元素数据:
3.修改嵌套对象深层的属性:
示例:通过ref实现对象类型数据的转变
ref不仅可以实现基本类型的数据转变,同时还支持对象类型数据的转变;实际上ref实现对象类型的转变本质上还是调用了reative函数来实现的;另外通过ref实现对象类型数据转变时,此对象如果是多层嵌套的对象(嵌套层数大于3层)时,就不推荐使用ref了,因为会降低执行效率;
<template><h2>汽车:{{Car.brand}}</h2><h2>价值:{{Car.price}}</h2><h2>游戏:</h2><ul><li v-for="item in games " :key="item.id">{{item.game}}</li></ul><h2>嵌套对象:{{obj.a.b.c.d}}</h2><button @click="ChangeCarBrand">点击修改汽车</button><button @click="ChangeCarPrice">点击修改汽车</button><button @click="changeFirstGame">点击修改第一个游戏</button><button @click="changeObjValue">点击修改嵌套对象深层的属性值</button>
</template><script lang="ts" setup name="Cars">
import {reactive, ref} from "vue";let Car=ref({brand:'奥迪',price:400000})
let games=ref([{id:'1001',game:'金铲铲',},{id:'1002',game:'云顶之弈',},{id:'1003',game:'英雄联盟',},
])//嵌套对象
let obj=ref({a:{b:{c:{d:9999}}}
})const ChangeCarBrand=()=>{Car.value.brand='宝马';}
const ChangeCarPrice=()=>{Car.value.price=660000;
}
const changeFirstGame=()=>{games.value[0].game='三星五费';
}const changeObjValue=()=>{obj.value.a.b.c.d+=100;
}
</script><style scoped></style>
在上述案例中我们通过ref函数来改变对象类型数据为响应式,此时它的执行逻辑是这样的:当调用ref函数并传入一个对象类型数据时,首先会生成一个refimpl的对象,此对象中包含一个value属性,此属性的值即为原始的对象类型数据;之后vue3会再次调用reative函数生成一个proxy代理对象也指向原始的的对象类型数据,因此说ref改变对象类型数据为响应式本质上还是reative函数;
测试结果展示:
1.改变对象属性:
2.修改数组元素:
3.修改对象深层嵌套属性:
3.vue3中的数据响应失效:
概述:
在通过reative改变对象类型数据为响应式时,如果直接改变整个对象,而不是对象的某些属性,此时就会造成响应失效;
案例:
<template><h2>汽车:{{Car.brand}}</h2><h2>价值:{{Car.price}}</h2>
<button @click="changeCar">点击修改汽车(响应失效)</button>
</template><script lang="ts" setup name="Car">
import {reactive, ref} from "vue";let Car=reactive({brand:'奥迪',price:400000,})//响应失效const changeCar=()=>{Car={brand:'宝马',price:660000,}console.log(Car)}</script><style scoped></style>
在上述案例中,我们通过reative函数创建了一个原始对象类型的数据Car,然后通过单击事件直接改变Car对象,就会造成响应失效;其原因是因为:当使用 `reactive` 创建了 `Car` 对象后,`Car` 实际上是一个被 `Proxy` 包装的响应式对象。而这里将一个新的非响应式对象赋值给 `Car`,就破坏了原来的响应式引用。因此赋值后的Car对象就不再是一个响应式对象了,因此在修改此对象时,不会同步渲染到页面上;
解决方案:
方式1:通过ref创建对象类型数据
<template><h2>汽车:{{Car.brand}}</h2><h2>价值:{{Car.price}}</h2><button @click="changeCar">点击修改汽车(非响应失效)</button></template><script lang="ts" setup name="Car">
import {reactive, ref} from "vue";let Car=ref({brand:'奥迪',price:400000,})const changeCar=()=>{Car.value={brand:'宝马',price:660000}
}</script><style scoped></style>
上述案例是通过ref函数来创建原始对象类型数据的,通过此种方式创建的对象是响应式的,并且在修改整个对象时,也不会造成响应失效;原因是因为:对于Car = ref({ brand: '奥迪', price: 400000 })
,Car
是一个Ref
类型的对象,其实际的数据存储在.value
属性中,这个.value
属性中的对象是被处理为响应式的;在changeCar
函数中,Car.value = { brand: '宝马', price: 660000 }
这样的操作是有效的。
因为Car
是一个Ref
对象,当修改其.value
属性时,Vue 的响应式系统会将此视为数据的更新。它会触发组件的重新渲染过程,重新计算模板中的表达式(如{{Car.brand}}
和{{Car.price}}
)。在这里,虽然整个对象被替换了,但ref
的响应式机制关注的是.value
属性的变化,只要通过.value
进行修改,它就能正确地通知相关的组件进行更新。
方式2;通过Object的assign方法:
此实现方式原理时将新旧对象的属性进行对比,将新对象中相同的属性的值修改到旧的对象上去,所以从本质上来说只是改变了对象的属性,并未改变对象类型数据本身;因此通过reative创建原始对象类型数据时,其代理对象proxy本身也没改变(只是代理对象的属性发生了改变),因此就不会造成响应失效;
<template><h2>汽车:{{Car.brand}}</h2><h2>价格:{{Car.price}}</h2><button @click="changeCar">点击修改汽车</button>
</template><script lang="ts" setup name="Carss">
import {reactive} from "vue";let Car=reactive({brand:'奥迪',price:400000,
})
const changeCar=()=>{Object.assign(Car,{brand:'宝马',price:660000})
}
</script><style scoped></style>
上述案例中:通过reative函数创建了原始对象类型数据Car,并通过单击鼠标事件修改整个对象,在回调函数中,调用了Object的assign方法,将新对象的属性复制到原始对象中,对原始对象的属性进行修改,因为并没有直接覆盖原始对象,因此其代理对象proxy不会发生改变,因此也不会造成响应失效;
4.toRef和toRefs函数:
1.概述:
toRef
函数用于创建一个对源响应式对象中某个属性的引用。它接受两个参数,第一个是源对象,第二个是要引用的属性名,通过此函数获取的对象的属性也为响应式(refimpl.value);toRefs
函数是将一个响应式对象的所有属性都转换为类似于toRef
创建的引用。它接受一个响应式对象作为参数。
2.案例:
<template><div class="person"><h2>姓名:{{ person.name }}</h2><h2>年龄:{{ person.age }}</h2><h2>性别:{{ person.gender }}</h2><button @click="changeName">修改名字</button><button @click="changeAge">修改年龄</button><button @click="changeGender">修改性别</button></div><div></div>
</template><script lang="ts" setup name="Person">import {reactive, ref, toRef, toRefs} from "vue";// ref rea
let person = reactive({name: '张三', age: 25, gender: "男"});
//通过toRefs将person对象中的n个属性 批量的取出来
let {name, gender} = toRefs(person);
//通过toRef将person对象中的一个属性 取出来
// 转换为 RefImpl 实例对象
let age = toRef(person, 'age');const changeName = () => {name.value += '~';
}
const changeAge = () => {age.value += 1;console.log(age)
}
const changeGender = () => {gender.value = "女";
}</script><style scoped></style>
上述案例中,我们通过reative创建了原始对象类型数据person,并通过toref函数和torefs函数取出了person对象的各个属性,由于取出的对象的属性也为响应式,因此我们可以对这些属性进行修改,并在页面实现同步渲染;