shallowRef
作用:创建一个响应式数据,但只对顶层属性进行响应式处理。
用法:
let myVar = shallowRef(initialValue);
特点:只跟踪引用值的变化,不关心值内部的属性变化。
shallowReactive
作用:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的
用法:
const myObj = shallowReactive({ ... });
特点:对象的顶层属性是响应式的,但嵌套对象的属性不是。
1.shallowRef 与 shallowReactive
我们直接使用代码解释如何使用。
新建App.vue实现了一个简单的求和和修改人物信息的功能。
我们使用 ref
创建了名为 sum
和 person
的响应式引用
changeSum
函数在点击按钮时将 sum
的值加1,changeName
函数将 person
的姓名修改为 "李四",changeAge
函数将 person
的年龄加1,changePerson
函数将整个 person
对象替换为一个新的对象。
展示:
当我点击按钮时:
现在,我将shallowRef来替换掉Ref
这是为什么呢?
因为shallowRef是浅层响应的意思,它只能调用第一层,深层次的则不行
你可以这么理解,就只能有一个.value,后面再跟就没有响应了。
而shallowReactive的作用跟shallowRef一样的,不赘述了
作用:
shallowRef
的作用是将一个普通的 JavaScript 对象或数组转换为响应式对象,但不会递归地将对象或数组中的每个属性或元素都转换为响应式。这意味着当你修改对象或数组的属性或元素时,不会触发视图的更新。与之相反,
ref
会将对象或数组的每个属性或元素都转换为响应式。这意味着当你修改对象或数组的属性或元素时,视图将会自动更新。换言之,如果我们修改了对象内部属性或数组内部元素,视图不会自动更新。只有当我们修改了对象或数组本身时,视图才会更新以反映这种变化。这种行为适用于当我们只关心对象或数组的引用变化,而不关心其内部属性或元素的具体变化时。这样可以提高性能,减少不必要的监测和更新操作。
总而言之:
通过使用 shallowRef() 和 shallowReactive() 来绕开深度响应。浅层式 API
创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。
2.readonly 与 shallowReadonly
-
作用:用于创建一个对象的深只读副本。
-
用法:
const original = reactive({ ... }); const readOnlyCopy = readonly(original);
-
特点:
-
对象的所有嵌套属性都将变为只读。
-
任何尝试修改这个对象的操作都会被阻止(在开发模式下,还会在控制台中发出警告)。
-
readonly
和 shallowReadonly
是 Vue 3 中提供的用于创建只读的响应式引用的函数。
概念:
想象一下,你有一个对象或数组,你希望它在被使用的时候不能被修改。就好像你给别人一个只读的物品,他们可以查看它,但不能改变它的内容。
readonly
的作用就是将一个对象或数组转换为只读的响应式引用。这意味着当你使用 readonly
创建的变量时,它是只读的,你不能直接修改它的值。如果你尝试修改它,Vue 3 会发出警告,并且不会更新视图。
shallowReadonly
与 readonly
类似,也是用于创建只读的响应式引用,但它是浅层只读的。这意味着,如果你使用 shallowReadonly
创建的变量是一个对象或数组,你可以阅读它的属性或元素,但不能修改属性或元素的值。然而,如果属性或元素是对象或数组,你可以修改它们的值。这样做是为了保持引用的只读特性,同时允许引用内部的对象或数组进行更改。
readonly
:
下面运用例子解释:
这段代码是一个网页应用,有两个按钮和两个显示结果的文本。你可以点击按钮来增加两个求和的结果。
甚至你会注意代码第22行报错,就是因为sum2提示只能读不能改。
报错的原因简单讲就是,里面不能是数据类型的参数,要放入的是具有响应式的参数,同时要把后面的.value去掉。
其中,sum1
是一个可以被修改的变量,表示一个求和结果。而 sum2
是一个只读的变量,它的值始终与 sum1
相同,但不能直接修改它。
当你点击 "点我sum1+1" 按钮时,sum1
的值会加1,而页面上显示的结果会更新。但是,当你点击 "点我sum2+1" 按钮时,尽管 sum2
的值与 sum1
相同,但它是只读的,不能直接修改它。因此,点击这个按钮并不会改变 sum2
的值,也不会更新页面上的结果。
这在某些情况下很有用,例如当你想向其他组件传递数据,但不希望其他组件能够修改它。readonly
可以确保数据的稳定性和可靠性,避免不必要的副作用。
展示:
补充:
报错的原因简单讲就是,里面不能是数据类型的参数,要放入的是具有响应式的参数,同时要把后面的.value去掉。
shallowReadonly:
作用:与 readonly
类似,但只作用于对象的顶层属性。
用法:
const original = reactive({ ... });
const shallowReadOnlyCopy = shallowReadonly(original);
特点:
-
只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。
-
适用于只需保护对象顶层属性的场景。
下面举例子解释:
car1
是一个包含汽车信息的对象,包括品牌、选项等。car2
是一个浅层只读的变量,它与 car1
相关联,但只能修改其属性的值,而不能修改整个对象或其属性的引用。
App.vue代码:
<template>
<div class="app">
<h2>当前sum1为:{{ sum1 }}</h2>
<h2>当前sum2为:{{ sum2 }}</h2>
<h2>当前car1为:{{ car1 }}</h2>
<h2>当前car2为:{{ car2 }}</h2>
<button @click="changeSum1">点我sum1+1</button>
<button @click="changeSum2">点我sum2+1</button>
<button @click="changeBrand2">修改品牌(car2)</button>
<button @click="changeColor2">修改颜色(car2)</button>
<button @click="changePrice2">修改价格(car2)</button>
</div>
</template>
<script setup lang="ts" name="App">
import { ref,reactive,readonly,shallowReadonly } from "vue";
let sum1 = ref(0)
let sum2 = readonly(sum1)
let car1 = reactive({
brand:'奔驰',
options:{
color:'红色',
price:100
}
})
let car2 = shallowReadonly(car1)
function changeSum1(){
sum1.value += 1
}
function changeSum2(){
sum2.value += 1 //sum2是不能修改的
}
function changeBrand2(){
car2.brand = '宝马'
}
function changeColor2(){
car2.options.color = '绿色'
}
function changePrice2(){
car2.options.price += 10
}
</script>
<style scoped>
.app {
background-color: #ddd;
border-radius: 10px;
box-shadow: 0 0 10px;
padding: 10px;
}
button {
margin:0 5px;
}
</style>
3.toRaw 与 markRaw
关于toRaw:
-
作用:用于获取一个响应式对象的原始对象,
toRaw
返回的对象不再是响应式的,不会触发视图更新。官网描述:这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。
何时使用? —— 在需要将响应式对象传递给非
Vue
的库或外部系统时,使用toRaw
可以确保它们收到的是普通对象
下面举一个例子解释:
我们使用 reactive
函数创建了一个名为 person
的响应式对象,其中包含姓名和年龄的属性。然后,我们使用 toRaw
函数获取了 person
对象的原始非响应式版本,并将其保存在 rawPerson
变量中。
toRaw
函数用于获取一个响应式对象的原始对象,即没有被包装成响应式的普通对象。在这个例子中,我们将响应式的 person
对象传递给 toRaw
函数,它返回了一个与 person
相同属性和值的普通对象。
在控制台中,我们打印了 person
和 rawPerson
的值,以展示它们之间的区别。
展示:
注意:
不建议保存对原始对象的持久引用,请谨慎使用。
就是以上使用 toRaw
函数获取了 person
对象的原始非响应式的操作。谨慎使用是为了防止person和Rawperson搞混。
关于markRaw:
-
作用:标记一个对象,使其永远不会变成响应式的。
例如使用
mockjs
时,为了防止误把mockjs
变为响应式对象,可以使用markRaw
去标记mockjs
然后用markRaw()标记
所以被markRaw标记一个对象后,使其永远不会变成响应式的。
补充:
使用场景:使用mockjs
时,为了防止误把mockjs
变为响应式对象,可以使用 markRaw
去标记mockjs
Mock.js是一个用于生成随机数据和模拟HTTP请求响应的JavaScript库。它可以帮助前端开发人员在开发过程中模拟接口数据,以便更好地进行前后端分离和并行开发。
1.安装Mock.js
2.用mockjs标记
App.vue代码:
<template>
<div class="app">
<h2>姓名:{{ person.name }}</h2>
<h2>年龄:{{ person.age }}</h2>
<button @click="person.age += 1">修改年龄</button>
<hr>
<h2>{{ car2 }}</h2>
<button @click="car2.price += 10">点我价格+10</button>
</div>
</template>
<script setup lang="ts" name="App">
import { reactive,toRaw,markRaw } from "vue";
import mockjs from 'mockjs'
/* toRaw */
let person = reactive({
name:'tony',
age:18
})
// 用于获取一个响应式对象的原始对象
let rawPerson = toRaw(person)
// console.log('响应式对象',person)
// console.log('原始对象',rawPerson)
let car = markRaw({brand:'奔驰',price:100})
let car2 = reactive(car)
console.log(car)
console.log(car2)
let mockJs = markRaw(mockjs)
</script>
<style scoped>
.app {
background-color: #ddd;
border-radius: 10px;
box-shadow: 0 0 10px;
padding: 10px;
}
button {
margin:0 5px;
}
</style>
4.customRef
概念:
我们知道,当我们在Vue中使用
ref
函数创建响应式数据时,它会帮助我们包装一个值,并且在这个值发生变化时通知相关的组件进行更新。这样我们就可以在组件中直接使用这个响应式数据,并且可以轻松地对其进行修改和访问。而
customRef
就是一个更高级的工具,它允许我们自定义响应式引用的行为。我们可以定义一个自己的跟踪器逻辑,来决定何时触发更新、如何处理值的变化等等。这样可以满足一些特殊的需求,例如延迟更新、异步更新等。所以,你可以将
customRef
看作是一个可以根据你的需求定制的响应式引用。它给了你更多的自由度和控制权,让你可以实现一些定制化的响应式行为,以适应你的项目需求。
作用:创建一个自定义的ref
,并对其依赖项跟踪和更新触发进行逻辑控制。
举例子解释:
现在有一个业务需求,当我在输入框修改时,标延迟1s后才显示,但是普通的ref是会立即显示,没有办法,只能用customRef(自定义的ref)
以下步骤:
1.引入customRef包,写出customRef的回调函数
2.回调函数里面写出return
3.写出get(),set()
4.导入跟踪器(track)和触发器(trigger)
重点:
跟踪器(track)用于侦听数据的变化,而触发器(trigger)用于触发相应的操作。
5.在里面修改延迟1s的业务
代码解析:
调用clearTimeout(timer)
来清除之前的延时器(如果存在)。这样可以确保在设置新的延时器之前,之前的延时器已经被取消。然后,使用setTimeout
函数创建一个新的延时器,它会在1000毫秒(即1秒)后执行一个回调函数。
回调函数中的代码会将传入的value
赋给initValue
,即更新了数据。然后,通过trigger()
方法通知Vue数据msg
发生了变化,使其重新渲染相关的组件。
展示:
App.vue代码:
<template>
<div class="app">
<h2>{{ msg }}</h2>
<input type="text" v-model="msg">
</div>
</template>
<script setup lang="ts" name="App">
import {ref,customRef} from 'vue'
// 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
//let msg = ref('你好')
// 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
let initValue = '你好'
let timer:number
let msg = customRef((track,trigger)=>{
return{
// get何时调用? - msg被读取时
get(){
track();//告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
return initValue;
},
// set何时调用? - msg被修改时
set(value){
clearTimeout(timer)
timer = setTimeout(()=>{
initValue = value
trigger(); //通知Vue一下数据msg变化了
},1000);
}
}
})
</script>
<style scoped>
.app {
background-color: #ddd;
border-radius: 10px;
box-shadow: 0 0 10px;
padding: 10px;
}
button {
margin:0 5px;
}
</style>
但是这样写还是写不够方便简洁,以后有新的业务时要修改的地方太多
还可以更近一步的完善,如下:
1.封装一个hoolks,将逻辑代码写入进去。
代码解析:
方法会返回一个具有延迟更新功能的数据对象。你可以把它想象成一个计时器。
当你在组件中使用该方法时,你需要传递两个参数:初始值和延迟时间。初始值就是数据的初始内容,而延迟时间表示在用户输入后等待多长时间才进行更新。
在内部,这个函数使用了Vue提供的`customRef`函数,它可以帮助我们创建一个自定义的数据跟踪器。
这个数据跟踪器有两个方法:`get`和`set`。当其他部分代码读取数据时,就会调用`get`方法。然后,我们返回数据的当前值。
当其他部分代码修改数据时,就会调用`set`方法。在这个方法中,我们首先清除之前的计时器,以防止数据更新发生太频繁。然后,我们创建一个新的计时器,在延迟时间后执行一个回调函数。在回调函数中,我们将数据的值更新为新值,并通知Vue数据发生了变化。
最后,函数返回一个包含延迟更新的数据对象,你可以在组件中使用它。当你读取这个数据时,Vue会自动追踪它,并在数据发生变化时更新相关的组件。而当你修改这个数据时,它会等待一段时间后才进行更新操作,以避免过于频繁的更新。
2.在App.vue中使用该文件
useMsgRef.ts代码:
import { customRef } from "vue";
export default function(initValue:string,delay:number){
// 使用Vue提供的customRef定义响应式数据
let timer:number
// track(跟踪)、trigger(触发)
let msg = customRef((track,trigger)=>{
return {
// get何时调用?—— msg被读取时
get(){
track() //告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
return initValue
},
// set何时调用?—— msg被修改时
set(value){
clearTimeout(timer)
timer = setTimeout(() => {
initValue = value
trigger() //通知Vue一下数据msg变化了
}, delay);
}
}
})
return {msg}
}
App.vue代码:
<template>
<div class="app">
<h2>{{ msg }}</h2>
<input type="text" v-model="msg">
</div>
</template>
<script setup lang="ts" name="App">
import {ref} from 'vue'
import useMsgRef from './useMsgRef'
// 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
// let msg = ref('你好')
// 使用useMsgRef来定义一个响应式数据且有延迟效果
let {msg} = useMsgRef('你好',3000)
</script>
<style scoped>
.app {
background-color: #ddd;
border-radius: 10px;
box-shadow: 0 0 10px;
padding: 10px;
}
button {
margin:0 5px;
}
</style>
总结:
本文介绍了一些Vue 3中的响应式API,包括shallowRef
、shallowReactive
、readonly
、shallowReadonly
、toRaw
、markRaw
和customRef
。
shallowRef
和shallowReactive
是用于创建响应式数据的函数。shallowRef
创建一个浅层响应式引用,而shallowReactive
创建一个浅层响应式对象。readonly
和shallowReadonly
用于创建只读的响应式数据。readonly
创建一个深层只读响应式对象,而shallowReadonly
创建的对象只有顶层属性是只读的。toRaw
和markRaw
用于处理响应式数据的原始值。toRaw
用于获取响应式对象的原始值,而markRaw
用于标记一个对象,使其在响应式转换时保持不可响应。customRef
是一个自定义的响应式引用创建函数,允许开发者定义自己的跟踪器逻辑,实现更高级的响应式行为。