Vue3-5-响应式数据处理
shallowReactive与shallowRef(浅拷贝),readonly与shallowReadinly(只读),toRaw与markRaw(转换为普通对象),toRef(新建对象),customRef(自定义),provide与inject,响应式数据判断
·
shallowReactive 与 shallowRef(浅拷贝)
- shallowReactive : 只处理了对象内最外层属性的响应式(也就是浅响应式)
- shallowRef: 只处理了value的响应式, 不进行对象的reactive处理
- 什么时候用浅响应式呢?
- 一般情况下使用ref和reactive即可
- 如果有一个对象数据, 结构比较深, 但变化时只是外层属性变化 ===> shallowReactive
- 如果有一个对象数据, 后面会产生新的对象来替换 ===> shallowRef
<template>
<h1>shallowReactive 与 shallowRef</h1>
<h2>{{m1}}</h2>
<h2>{{m2}}</h2>
<h2>{{m3}}</h2>
<h2>{{m4}}</h2>
<hr>
<button @click="updata">点击</button>
</template>
<script lang="ts">
import {onMounted, reactive, shallowReactive, ref, toRefs, shallowRef} from "vue";
export default ({
name:'App',
setup(){
// 深度劫持(响应)
const m1=reactive({
name:'jack',
agr:20,
car:{
name:'宝马',
color:'red'
}
})
// 浅度劫持(响应)
const m2=shallowReactive ({
name:'jack',
agr:20,
car:{
name:'宝马',
color:'red'
}
})
const m3=ref({
name:'jack',
agr:20,
car:{
name:'宝马',
color:'red'
}
}) // ref传对象时,里面会进行reactive处理
const m4=shallowRef({
name:'jack',
agr:20,
car:{
name:'宝马',
color:'red'
}
})
const updata = () => {
// m1.name +="=="
// m1.car.name +="==" // 两个都能执行说明是深度响应
// m2.name +="=="
// m2.car.name +="==" // 第二个不都能执行说明是浅度响应,对浅层数据能够进行修改
// m3.value.name +="=="
// m3.value.car.name +="==" // 两个都能执行说明是深度响应
// m4.value.name +="=="
// m4.value.car.name +="==" // 两个都不都能执行说明是浅度响应 m4类型是Object,不能进行响应式操作(数据不能修改)
}
return{
m1,m2,m3,m4,updata,
}
}
})
</script>
readonly 与 shallowReadonly
- readonly:深度只读数据
- 获取一个对象 (响应式或纯对象) 或 ref 并返回原始代理的只读代理
- 只读代理是深层的:访问的任何嵌套 property 也是只读的。
- shallowReadonly:浅只读数据
- 浅只读数据创建一个代理,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换
- 应用场景:在某些特定情况下, 我们可能不希望对数据进行更新的操作, 那就可以包装生成一个只读代理对象来读取数据, 而不能修改或删除
<template>
<h1>shallowReactive 与 shallowRef</h1>
<h2>{{m2}}</h2>
<h1>{{m3}}</h1>
<hr>
<button @click="updata">点击</button>
</template>
<script lang="ts">
import {reactive,readonly,shallowReadonly} from "vue";
export default ({
name:'App',
setup() {
const m1 = reactive({
name: 'jack',
agr: 20,
car: {
name: '宝马',
color: 'red'
}
})
const m2=readonly(m1)
const m3=shallowReadonly(m1)
const updata = () => {
// m2.name+='=='
// m2.car.name+='=='
// m3.name+='=='
m3.car.name+='=='
/*
* readonly的只读是深层次的,所有数据都不能修改
* shallowReadonly的只读是浅层次的,里面的数据是可以修改的
*/
}
return{
m2,m3,updata,
}
}
})
</script>
toRaw 与 markRaw
- toRaw返回由 reactive 或 readonly 方法转换成响应式代理的普通对象
- 这是一个还原方法,可用于临时读取,访问不会被代理/跟踪,写入时也不会触发界面更新
- markRaw标记一个对象,使其永远不会转换为代理。返回对象本身
- 应用场景:
有些值不应被设置为响应式的,例如复杂的第三方类实例或 Vue 组件对象
当渲染具有不可变数据源的大列表时,跳过代理转换可以提高性能
<template>
<h1>toRaw 与 markRaw</h1>
<h2>{{m1}}</h2>
<hr>
<button @click="clicktoRaw">点击 toRaw </button>
<button @click="clickmarkRaw">点击 markRaw </button>
</template>
<script lang="ts">
import {reactive,toRaw,markRaw} from "vue";
interface IPerson{
name:String,
age:Number,
car?:String[]
}
export default ({
name:'App',
setup() {
const m1 = reactive<IPerson>({
name: 'jack',
age: 20,
})
const clicktoRaw = () => {
const user=toRaw(m1)
user.name+="==" // 点击时不会发生反应,因为转化为了普通对象
}
const clickmarkRaw = () => {
// m1.car=['宝马', 'red']
// m1.car[0]+='==' // 点击时可以更改,说明添加操作让其转化为Proxy代理
const car=['宝马', 'red']
m1.car=markRaw(car)
// markRaw处理后就不能成为代理对象了
setTimeout(() => {
if(m1.car){
m1.car[0] += '=='
console.log('执行中')
}
}, 1000)
}
return {
m1,clickmarkRaw,clicktoRaw
}
}
})
</script>
toRef
- 为源响应式对象上的某个属性创建一个 ref对象, 二者内部操作的是同一个数据值, 更新时二者是同步的
- 区别ref: 拷贝了一份新的数据值单独操作, 更新时相互不影响
- 应用: 当要将 某个prop 的 ref 传递给复合函数时,toRef 很有用
<template>
<h1>clicktoRef</h1>
<h2>{{m1}}</h2>
<hr>
<button @click="clicktoRef">点击 clicktoRef </button>
</template>
<script lang="ts">
import {reactive, ref, toRef,} from "vue";
export default ({
name:'App',
setup() {
const m1 = reactive({
name: 'jack',
age: 20,
})
// 把响应式数据m1 中name 属性变成ref对象
const name=toRef(m1,'name')
// 把响应时数据进行包装变成ref对象
const age=ref(m1.age)
const clicktoRef = () => {
console.log('测试')
// m1.name +='=='
// name.value +='==' // 两个都会改变
// m1.age +=2
age.value +=2 // 第二个不会改变,说明ref拷贝后的值互不影响
}
return {
m1,clicktoRef
}
}
})
</script>
customRef
- 创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
- 自定义hook函数防抖
<template>
<h1>customRef</h1>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script lang="ts">
import {customRef, reactive, ref, toRef,} from "vue";
// 自定义防抖函数
// value传入数据不确定用泛型,time防抖时间
function useAntiShake<T>(val:T,delay=500){
let timeOutId: number // 准备一个存储定时器ID变量
return customRef((track,trigger)=>{
return{
// 返回数据的
get(){
// 告诉VUE追踪数据
track()
return val
},
set(newVal:T){
// 清理定时器
clearTimeout(timeOutId)
// 开启定时器
timeOutId=setTimeout(()=>{
val=newVal
trigger()
},delay)
}
}
})
}
export default ({
name:'App',
setup() {
// const keyword=ref('1')
const keyword=useAntiShake('123',1000)
return {
keyword
}
}
})
</script>
provide 与 inject
- provide和inject提供依赖注入,功能类似 2.x 的provide/inject
- 实现跨层级组件(祖孙)间通信
- app.vue
<template>
<h1>provide 与 inject</h1>
<h2>{{color}}</h2>
<button @click="color='red'">点击</button>
<button @click="color='yellow'">点击</button>
<button @click="color='pink'">点击</button>
<hr>
<son></son>
</template>
<script lang="ts">
import {reactive, ref, defineComponent, provide} from "vue";
import son from './components/son.vue'
export default defineComponent({
name:'App',
components:{
son,
},
setup() {
const color=ref('red')
provide('color',color)
return {
color
}
}
})
</script>
- son.vue
<template>
<h2>son组件</h2>
<hr>
<grandson></grandson>
</template>
<script>
import grandson from "./grandSon.vue";
export default {
name: "son",
components: {
grandson
}
}
</script>
<style scoped>
</style>
- grandson.vue
<template>
<h2 :style="{color}">grandson组件</h2>
</template>
<script>
import {inject} from "vue";
export default {
name: "grandSon",
setup(){
const color=inject('color')
return{
color,
}
}
}
</script>
<style scoped>
</style>
响应式数据的判断
- isRef: 检查一个值是否为一个 ref 对象
- isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
- isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
- isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理
更多推荐
已为社区贡献3条内容
所有评论(0)