Vue3从入门到实战:深度了解相关API

深藏阁楼爱情的钟 2024-04-26 01:52 181阅读 0赞

shallowRef

  1. 作用:创建一个响应式数据,但只对顶层属性进行响应式处理。
  2. 用法:

    1. let myVar = shallowRef(initialValue);
  3. 特点:只跟踪引用值的变化,不关心值内部的属性变化。

shallowReactive

  1. 作用:创建一个浅层响应式对象,只会使对象的最顶层属性变成响应式的,对象内部的嵌套属性则不会变成响应式的
  2. 用法:

    1. const myObj = shallowReactive({ ... });
  3. 特点:对象的顶层属性是响应式的,但嵌套对象的属性不是。

1.shallowRef 与 shallowReactive

我们直接使用代码解释如何使用。

新建App.vue实现了一个简单的求和和修改人物信息的功能。

78cb39d006ff4de582d92eb08b0ff861.png

我们使用 ref 创建了名为 sumperson 的响应式引用

changeSum 函数在点击按钮时将 sum 的值加1,changeName 函数将 person 的姓名修改为 “李四”,changeAge 函数将 person 的年龄加1,changePerson 函数将整个 person 对象替换为一个新的对象。

展示:

ea56a16a3b2a4db295d986d22ecff8ef.png

当我点击按钮时:

8ca0e7b3b8364d568aa262c9c8d0fe4c.png

3906e97d985b4ff1b6b584a4496422f7.png

现在,我将shallowRef来替换掉Ref

6f83fd3384384da09815b54a53045cc8.png

75184742b98e4406b3f22c781e19ab39.png

这是为什么呢?

因为shallowRef是浅层响应的意思,它只能调用第一层,深层次的则不行

38feb90ee2154fb78086ef5562faac6d.png

你可以这么理解,就只能有一个.value,后面再跟就没有响应了。

而shallowReactive的作用跟shallowRef一样的,不赘述了

作用:

shallowRef 的作用是将一个普通的 JavaScript 对象或数组转换为响应式对象,但不会递归地将对象或数组中的每个属性或元素都转换为响应式。这意味着当你修改对象或数组的属性或元素时,不会触发视图的更新。

与之相反,ref 会将对象或数组的每个属性或元素都转换为响应式。这意味着当你修改对象或数组的属性或元素时,视图将会自动更新。

换言之,如果我们修改了对象内部属性或数组内部元素,视图不会自动更新。只有当我们修改了对象或数组本身时,视图才会更新以反映这种变化。这种行为适用于当我们只关心对象或数组的引用变化,而不关心其内部属性或元素的具体变化时。这样可以提高性能,减少不必要的监测和更新操作。

总而言之:

通过使用 shallowRef() 和 shallowReactive() 来绕开深度响应。浅层式 API 创建的状态只在其顶层是响应式的,对所有深层的对象不会做任何处理,避免了对每一个内部属性做响应式所带来的性能成本,这使得属性的访问变得更快,可提升性能。


2.readonly 与 shallowReadonly

  1. 作用用于创建一个对象的深只读副本。
  2. 用法

    1. const original = reactive({ ... });
    2. const readOnlyCopy = readonly(original);
  3. 特点

    • 对象的所有嵌套属性都将变为只读。
    • 任何尝试修改这个对象的操作都会被阻止(在开发模式下,还会在控制台中发出警告)。

readonly shallowReadonly 是 Vue 3 中提供的用于创建只读的响应式引用的函数。

概念:

想象一下,你有一个对象或数组,你希望它在被使用的时候不能被修改。就好像你给别人一个只读的物品,他们可以查看它,但不能改变它的内容。

readonly 的作用就是将一个对象或数组转换为只读的响应式引用。这意味着当你使用 readonly 创建的变量时,它是只读的,你不能直接修改它的值。如果你尝试修改它,Vue 3 会发出警告,并且不会更新视图。

shallowReadonlyreadonly 类似,也是用于创建只读的响应式引用,但它是浅层只读的。这意味着,如果你使用 shallowReadonly 创建的变量是一个对象或数组,你可以阅读它的属性或元素,但不能修改属性或元素的值。然而,如果属性或元素是对象或数组,你可以修改它们的值。这样做是为了保持引用的只读特性,同时允许引用内部的对象或数组进行更改。

readonly

下面运用例子解释:

这段代码是一个网页应用,有两个按钮和两个显示结果的文本。你可以点击按钮来增加两个求和的结果。

459dd315bdd64e169b08ea642bad888b.png

甚至你会注意代码第22行报错,就是因为sum2提示只能读不能改

092278f3a07c420dbcad3c38be17a24e.png

报错的原因简单讲就是,里面不能是数据类型的参数,要放入的是具有响应式的参数,同时要把后面的.value去掉。

其中,sum1 是一个可以被修改的变量,表示一个求和结果。而 sum2 是一个只读的变量,它的值始终与 sum1 相同,但不能直接修改它。

当你点击 “点我sum1+1” 按钮时,sum1 的值会加1,而页面上显示的结果会更新。但是,当你点击 “点我sum2+1” 按钮时,尽管 sum2 的值与 sum1 相同,但它是只读的,不能直接修改它。因此,点击这个按钮并不会改变 sum2 的值,也不会更新页面上的结果。

这在某些情况下很有用,例如当你想向其他组件传递数据,但不希望其他组件能够修改它。readonly 可以确保数据的稳定性和可靠性,避免不必要的副作用。

展示:

80c336d6afed4afab769552206c824fe.png

补充:

092278f3a07c420dbcad3c38be17a24e.png

报错的原因简单讲就是,里面不能是数据类型的参数,要放入的是具有响应式的参数,同时要把后面的.value去掉。


shallowReadonly:

作用:与 readonly 类似,但只作用于对象的顶层属性。

用法:

  1. const original = reactive({ ... });
  2. const shallowReadOnlyCopy = shallowReadonly(original);

特点:

  • 只将对象的顶层属性设置为只读,对象内部的嵌套属性仍然是可变的。
  • 适用于只需保护对象顶层属性的场景。

下面举例子解释:

6af57aab6e5344489a63c4f9f979adc2.png

car1 是一个包含汽车信息的对象,包括品牌、选项等。car2 是一个浅层只读的变量,它与 car1 相关联,但只能修改其属性的值,而不能修改整个对象或其属性的引用。

65e2cb9bc73f46898ee018c5be6dca6d.png

App.vue代码:

  1. <template>
  2. <div class="app">
  3. <h2>当前sum1为:{
  4. { sum1 }}</h2>
  5. <h2>当前sum2为:{
  6. { sum2 }}</h2>
  7. <h2>当前car1为:{
  8. { car1 }}</h2>
  9. <h2>当前car2为:{
  10. { car2 }}</h2>
  11. <button @click="changeSum1">点我sum1+1</button>
  12. <button @click="changeSum2">点我sum2+1</button>
  13. <button @click="changeBrand2">修改品牌(car2)</button>
  14. <button @click="changeColor2">修改颜色(car2)</button>
  15. <button @click="changePrice2">修改价格(car2)</button>
  16. </div>
  17. </template>
  18. <script setup lang="ts" name="App">
  19. import { ref,reactive,readonly,shallowReadonly } from "vue";
  20. let sum1 = ref(0)
  21. let sum2 = readonly(sum1)
  22. let car1 = reactive({
  23. brand:'奔驰',
  24. options:{
  25. color:'红色',
  26. price:100
  27. }
  28. })
  29. let car2 = shallowReadonly(car1)
  30. function changeSum1(){
  31. sum1.value += 1
  32. }
  33. function changeSum2(){
  34. sum2.value += 1 //sum2是不能修改的
  35. }
  36. function changeBrand2(){
  37. car2.brand = '宝马'
  38. }
  39. function changeColor2(){
  40. car2.options.color = '绿色'
  41. }
  42. function changePrice2(){
  43. car2.options.price += 10
  44. }
  45. </script>
  46. <style scoped>
  47. .app {
  48. background-color: #ddd;
  49. border-radius: 10px;
  50. box-shadow: 0 0 10px;
  51. padding: 10px;
  52. }
  53. button {
  54. margin:0 5px;
  55. }
  56. </style>

3.toRaw 与 markRaw

关于toRaw:

  1. 作用:用于获取一个响应式对象的原始对象, toRaw 返回的对象不再是响应式的,不会触发视图更新。

    官网描述:这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法。不建议保存对原始对象的持久引用,请谨慎使用。

    何时使用? —— 在需要将响应式对象传递给非 Vue 的库或外部系统时,使用 toRaw 可以确保它们收到的是普通对象

下面举一个例子解释:

cf47dc8fce244af69e380eee748525d5.png

我们使用 reactive 函数创建了一个名为 person 的响应式对象,其中包含姓名和年龄的属性。然后,我们使用 toRaw 函数获取了 person 对象的原始非响应式版本,并将其保存在 rawPerson 变量中。

toRaw 函数用于获取一个响应式对象的原始对象,即没有被包装成响应式的普通对象。在这个例子中,我们将响应式的 person 对象传递给 toRaw 函数,它返回了一个与 person 相同属性和值的普通对象。

在控制台中,我们打印了 personrawPerson 的值,以展示它们之间的区别。

展示:

3bc9d6d0639a4a90b13ec4d4d2ed8153.png

注意:

不建议保存对原始对象的持久引用,请谨慎使用。

就是以上使用 toRaw 函数获取了 person 对象的原始非响应式的操作。谨慎使用是为了防止person和Rawperson搞混。


关于markRaw:

  1. 作用标记一个对象,使其永远不会变成响应式的。

    例如使用mockjs时,为了防止误把mockjs变为响应式对象,可以使用 markRaw 去标记mockjs

8c4eb5acd3f84599bd2797bdc2289444.png

f9df89079d9d45f5a29f96d06503c681.png

然后用markRaw()标记

e817df0a09af430093cc6e998ec6ca6e.png

cabebac9608e4510a6c356c39a4187b8.png

所以被markRaw标记一个对象后,使其永远不会变成响应式的。

补充:

使用场景:使用mockjs时,为了防止误把mockjs变为响应式对象,可以使用 markRaw 去标记mockjs

Mock.js是一个用于生成随机数据和模拟HTTP请求响应的JavaScript库。它可以帮助前端开发人员在开发过程中模拟接口数据,以便更好地进行前后端分离和并行开发。

1.安装Mock.jsb5ffb4cd4abf432f89d763fd7126094d.png

2.用mockjs标记

35de2b2f2b4644ffa68bba0a3a5fc5a3.png

App.vue代码:

  1. <template>
  2. <div class="app">
  3. <h2>姓名:{
  4. { person.name }}</h2>
  5. <h2>年龄:{
  6. { person.age }}</h2>
  7. <button @click="person.age += 1">修改年龄</button>
  8. <hr>
  9. <h2>{
  10. { car2 }}</h2>
  11. <button @click="car2.price += 10">点我价格+10</button>
  12. </div>
  13. </template>
  14. <script setup lang="ts" name="App">
  15. import { reactive,toRaw,markRaw } from "vue";
  16. import mockjs from 'mockjs'
  17. /* toRaw */
  18. let person = reactive({
  19. name:'tony',
  20. age:18
  21. })
  22. // 用于获取一个响应式对象的原始对象
  23. let rawPerson = toRaw(person)
  24. // console.log('响应式对象',person)
  25. // console.log('原始对象',rawPerson)
  26. let car = markRaw({brand:'奔驰',price:100})
  27. let car2 = reactive(car)
  28. console.log(car)
  29. console.log(car2)
  30. let mockJs = markRaw(mockjs)
  31. </script>
  32. <style scoped>
  33. .app {
  34. background-color: #ddd;
  35. border-radius: 10px;
  36. box-shadow: 0 0 10px;
  37. padding: 10px;
  38. }
  39. button {
  40. margin:0 5px;
  41. }
  42. </style>

4.customRef

概念:

我们知道,当我们在Vue中使用ref函数创建响应式数据时,它会帮助我们包装一个值,并且在这个值发生变化时通知相关的组件进行更新。这样我们就可以在组件中直接使用这个响应式数据,并且可以轻松地对其进行修改和访问。

customRef就是一个更高级的工具,它允许我们自定义响应式引用的行为。我们可以定义一个自己的跟踪器逻辑,来决定何时触发更新、如何处理值的变化等等。这样可以满足一些特殊的需求,例如延迟更新、异步更新等。

所以,你可以将customRef看作是一个可以根据你的需求定制的响应式引用。它给了你更多的自由度和控制权,让你可以实现一些定制化的响应式行为,以适应你的项目需求。

作用创建一个自定义的ref,并对其依赖项跟踪和更新触发进行逻辑控制。

举例子解释:

7a097e083995442e9aef41e97c57ae99.png

63d64131f2234f3cb9cc2c63a3551afc.png

现在有一个业务需求,当我在输入框修改时,标延迟1s后才显示,但是普通的ref是会立即显示,没有办法,只能用customRef(自定义的ref)

以下步骤:

1.引入customRef包,写出customRef的回调函数

cf620f5801e24aef95998bd4f3b7a694.png

2.回调函数里面写出return

d638bc0742e9438f9d7c93f88f5c8725.png

3.写出get(),set()

2668b61cda66424889389972abaa1a9f.png

4.导入跟踪器(track)和触发器(trigger)

411883b4d2c44a09ba2edaa76d3fa0d3.png

重点:

跟踪器(track)用于侦听数据的变化,而触发器(trigger)用于触发相应的操作。

5.在里面修改延迟1s的业务

5d0e3cc238e24a5a9ec1ae4e3dc13545.png

代码解析:

调用clearTimeout(timer)来清除之前的延时器(如果存在)。这样可以确保在设置新的延时器之前,之前的延时器已经被取消。然后,使用setTimeout函数创建一个新的延时器,它会在1000毫秒(即1秒)后执行一个回调函数。

回调函数中的代码会将传入的value赋给initValue,即更新了数据。然后,通过trigger()方法通知Vue数据msg发生了变化,使其重新渲染相关的组件。

展示:

50198ead61ea490dbab958e55df10edb.png


App.vue代码:

  1. <template>
  2. <div class="app">
  3. <h2>{
  4. { msg }}</h2>
  5. <input type="text" v-model="msg">
  6. </div>
  7. </template>
  8. <script setup lang="ts" name="App">
  9. import {ref,customRef} from 'vue'
  10. // 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
  11. //let msg = ref('你好')
  12. // 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
  13. let initValue = '你好'
  14. let timer:number
  15. let msg = customRef((track,trigger)=>{
  16. return{
  17. // get何时调用? - msg被读取时
  18. get(){
  19. track();//告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
  20. return initValue;
  21. },
  22. // set何时调用? - msg被修改时
  23. set(value){
  24. clearTimeout(timer)
  25. timer = setTimeout(()=>{
  26. initValue = value
  27. trigger(); //通知Vue一下数据msg变化了
  28. },1000);
  29. }
  30. }
  31. })
  32. </script>
  33. <style scoped>
  34. .app {
  35. background-color: #ddd;
  36. border-radius: 10px;
  37. box-shadow: 0 0 10px;
  38. padding: 10px;
  39. }
  40. button {
  41. margin:0 5px;
  42. }
  43. </style>

但是这样写还是写不够方便简洁,以后有新的业务时要修改的地方太多

还可以更近一步的完善,如下:

1.封装一个hoolks,将逻辑代码写入进去。

b55d9e185c16414ca34dccb0b5e63a28.png

代码解析:

方法会返回一个具有延迟更新功能的数据对象。你可以把它想象成一个计时器。

当你在组件中使用该方法时,你需要传递两个参数:初始值和延迟时间。初始值就是数据的初始内容,而延迟时间表示在用户输入后等待多长时间才进行更新。

在内部,这个函数使用了Vue提供的`customRef`函数,它可以帮助我们创建一个自定义的数据跟踪器。

这个数据跟踪器有两个方法:`get`和`set`。当其他部分代码读取数据时,就会调用`get`方法。然后,我们返回数据的当前值。

当其他部分代码修改数据时,就会调用`set`方法。在这个方法中,我们首先清除之前的计时器,以防止数据更新发生太频繁。然后,我们创建一个新的计时器,在延迟时间后执行一个回调函数。在回调函数中,我们将数据的值更新为新值,并通知Vue数据发生了变化。

最后,函数返回一个包含延迟更新的数据对象,你可以在组件中使用它。当你读取这个数据时,Vue会自动追踪它,并在数据发生变化时更新相关的组件。而当你修改这个数据时,它会等待一段时间后才进行更新操作,以避免过于频繁的更新。

2.在App.vue中使用该文件

d1c1db282e67425e9294d48ed4a78d34.png

c383378e4f094591852147ecc5f9bc9d.png

useMsgRef.ts代码:

  1. import { customRef } from "vue";
  2. export default function(initValue:string,delay:number){
  3. // 使用Vue提供的customRef定义响应式数据
  4. let timer:number
  5. // track(跟踪)、trigger(触发)
  6. let msg = customRef((track,trigger)=>{
  7. return {
  8. // get何时调用?—— msg被读取时
  9. get(){
  10. track() //告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新
  11. return initValue
  12. },
  13. // set何时调用?—— msg被修改时
  14. set(value){
  15. clearTimeout(timer)
  16. timer = setTimeout(() => {
  17. initValue = value
  18. trigger() //通知Vue一下数据msg变化了
  19. }, delay);
  20. }
  21. }
  22. })
  23. return {msg}
  24. }

App.vue代码:

  1. <template>
  2. <div class="app">
  3. <h2>{
  4. { msg }}</h2>
  5. <input type="text" v-model="msg">
  6. </div>
  7. </template>
  8. <script setup lang="ts" name="App">
  9. import {ref} from 'vue'
  10. import useMsgRef from './useMsgRef'
  11. // 使用Vue提供的默认ref定义响应式数据,数据一变,页面就更新
  12. // let msg = ref('你好')
  13. // 使用useMsgRef来定义一个响应式数据且有延迟效果
  14. let {msg} = useMsgRef('你好',3000)
  15. </script>
  16. <style scoped>
  17. .app {
  18. background-color: #ddd;
  19. border-radius: 10px;
  20. box-shadow: 0 0 10px;
  21. padding: 10px;
  22. }
  23. button {
  24. margin:0 5px;
  25. }
  26. </style>

总结:

本文介绍了一些Vue 3中的响应式API,包括shallowRefshallowReactivereadonlyshallowReadonlytoRawmarkRawcustomRef

  • shallowRefshallowReactive是用于创建响应式数据的函数。shallowRef创建一个浅层响应式引用,而shallowReactive创建一个浅层响应式对象。
  • readonlyshallowReadonly用于创建只读的响应式数据。readonly创建一个深层只读响应式对象,而shallowReadonly创建的对象只有顶层属性是只读的。
  • toRawmarkRaw用于处理响应式数据的原始值。toRaw用于获取响应式对象的原始值,而markRaw用于标记一个对象,使其在响应式转换时保持不可响应。
  • customRef是一个自定义的响应式引用创建函数,允许开发者定义自己的跟踪器逻辑,实现更高级的响应式行为。

发表评论

表情:
评论列表 (有 0 条评论,181人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Vue2+3入门实战

    作为IT技术相关行业不可或缺的岗位之一,前端开发工程师就业前途广阔,一直是很多同学心中转行的首选行业。但很多人还没开始,便被一系列问题难倒了,比如:前端该如何入门?路线图