Vue3笔记_<进阶篇—下篇>

shallowRef和shallowReactive

vue的响应系统默认是深度的,虽然让状态管理非常可观,但是数据量巨大时深度响应会导致不小的性能负担,因为每个属性访问都将触发代理的依赖追踪。

  • shallowReactive:只处理对象最外层属性的响应式
  • shallowRef:只处理基本数据类型的数据,对象和数组类型不进行响应式处理

什么情况下使用浅响应式

  1. 如果一个对象数据,结构比较深,但变化时知识外层属性变化,那么就用shallowReactive
  2. 如果一个对象数据,后续功能不会修改该对象中的属性。而是产生新的对象来替换,那么就用shallowRef
<template>
    <h2>shallowRef</h2>
    <p>姓名:{{ obj.name }}</p>
    <p>年龄:{{ obj.age }}</p>
    <p>成绩:{{ obj.study.english.number }}</p>
    <button @click="obj.name+='--'">姓名</button>
    <button @click="obj.age+=1">年龄</button>
    <button @click="obj.study.english.number+=5">成绩</button>
    <hr>
    <h2>challowReactive</h2>
    <p>{{ str }}--{{ strobj.age }}---{{ shallow.age }}</p>
    <button @click="str++">ref++</button>
    <button @click="strobj.age++">strobj++</button>
    <button @click="shallow={age:999}">shallow</button>
</template>

<script setup>
import { shallowReactive,shallowRef,reactive,ref } from 'vue';
// const obj = reactive({
const obj = shallowReactive({    //shallowReactive 只处理对象最外层属性的响应式
    name:"凯撒",
    age:18,
    study:{  
        english:{
            number:22  //修改时生效,因为只处理最外层响应式数据,嵌套的数据不会处理
        }
    }
})
//shallowRef 只处理基本数据类型的数据,对象和数组类型不进行响应式处理
const str = ref(0)
const strobj = ref({ //也会生效,因为ref在处理对象或数组类型的时候会借用ES6的proxy方法
    age:20
})
const shallow = shallowRef({ //shallowRef在处理对象或数组类型的时候不会变成响应式
    age:20
})
console.log('strobj',strobj); //proxy数据

console.log('shallow',shallow); //object数据  使用shallowRef不会将对象进行响应式处理
console.log(obj)
</script>

shallowRef和shallowReactive使用误区!!!

使用误区看这篇文章:https://www.cnblogs.com/s-w-f/p/16589138.html


readonly和shallowReadOnly

readonly:接受一个对象(无论是响应式或者普通的)或是一个ref,返回一个原值的制度代理。是深度只读

shallowReadOnly:让一个响应式数据变成只读的,但是浅只读

应用场景:在某些特定的情况下,我们可能不希望对属性进行更新的操作,那就可以包专场一个只读代理对象来读取数据,而不能修改或删除

<template>
    <h2>readOnly</h2>
    <p>姓名:{{ obj.name }}</p>
    <p>年龄:{{ obj.age }}</p>
    <p>成绩:{{ obj.study.english.number }}</p>
    <button @click="obj.name+='--'">姓名</button>
    <button @click="obj.age+=1">年龄</button>
    <button @click="obj.study.english.number+=5">成绩</button>
    <hr>
    <h2>shallowReadOnly</h2>
    <p>{{ str }}--{{ strobj.age }}</p>
    <button @click="str++">ref++</button>
    <button @click="strobj.age++">strobj++</button>
</template>
<script setup>
import { readonly,shallowReadonly,reactive,ref } from 'vue';
//readonly接收一个对象或一个ref,返回一个只读代理(深度)。发送修改就会报警告
//shallowreadonly让一个响应式数据变只读,但是浅只读
//使用场景:在特定情况下,我们不希望数据更新,这个时候就可以将数据包装返回一个只读代理,而不能删除或修改
let obj = reactive({
    name:"凯撒",
    age:18,
    study:{  
        english:{
            number:22 
        }
    }
})
// obj = readonly(obj) //深度只读
obj = shallowReadonly(obj)  //浅只读:嵌套的数据还是可修改的响应式数据

let str = ref(0)
let strobj = ref({ //也会生效,因为ref在处理对象或数组类型的时候会借用ES6的proxy方法
    age:20
})
console.log('strobj',strobj); //proxy数据

console.log(obj)
</script>

响应数据判断和Raw

在vue2的模板中需要一个根节点<div></div>,但在Vue3中不需要根节点,内部会将多个标签包含在一个fragment虚拟元素中,减少标签层级,减少内存占用

  1. toRaw:将响应式对象(只针对对象类型)转化成普通数据(失去响应式)
  2. markraw:将数据标记为原始数据,永远不会成为响应式数据
  3. isRef:判断数据是否是ref包裹的数据
  4. isReactive:判断数据是否是reactive创建的响应式代理
  5. isReadonly:判断数据是否是readonly创建的只读代理
  6. isproxy:判断数据是否是reactive或readonly包裹的对象,readonly包裹的对象类型也会变成proxy数据

import {toRaw,markRaw,isProxy,isRef,isReactive,isReadonly, onMounted} from 'vue';
onMounted(()=>{
    // ----------- 响应式数据判断 -------------
    console.log(isReactive(obj));   //检查数据是否是reative创造的
    console.log(isReadonly(strobj)); //检查数据是否是readonly创造的
    console.log(isRef(str));    //检查数据是否是ref创造的
    console.log(isProxy(obj));  //检查数据是否是reactive或readonl创造的
    console.log(isProxy(strobj));
    console.log(isReactive( str ));
    console.log(isRef( str ));
    console.log(strobj);
})

Teleport瞬移DOM

<Teleport>是一个内置组件,它可以将一个组件内部的一部分模板传送到该组件的DOM结构外层的位置去。

应用场景:一个组件模板的一部分在逻辑上从属于该组件,但从整个应用视图的角度来看,它在DOM中应该被渲染在整个Vue应用外部的其他地方

注意:<Teleport>只改变了渲染的DOM结构,它不会影响组件间的逻辑关系。也减少说,如果<Teleport>包含了一个组件,那么该组件始终和这个使用了<Teleport>的组件保持逻辑上的父子关系。传入的props和触发的事件也会照常工作。这也就意味着来自父组件的注入也会按预期工作,子组件将在Vue Devtools中嵌套在父级组件下面,而不是放在实际内容移动到的地方

父组件
<template>
    <div class="father">
        <h2>父组件</h2>
        <sum></sum>
    </div>
</template>
<script setup>
import sum from './sun.vue'
</script>

<style scoped>
.father{
    background: red;
    padding: 10px;
}
</style>

子组件
<template>
    <div class="sun">
        <h3>子组件</h3>
        <!-- 遮罩层 -->
        <button @click="ble=true">{{ble}}</button>
        <!-- <teleport to='' /> -->
        <!-- 此组件可以将指定的dom元素瞬移指定的地方渲染 -->
        <!-- 仅仅只是渲染,在代码层面他的父子关系还会保存(父子传参之类还是有效的) -->
        <Teleport to="body"> <!-- 将元素渲染到body标签层 -->
            <div class="box" v-if="ble">
            <div class="msg">
                <p>这是一个弹窗</p>
                <button @click="ble=false">关闭</button>
            </div>
        </div>
        </Teleport>
    </div>
</template>
<script setup>
import { ref } from 'vue'
const ble = ref(false)
</script>
<style scoped>
.sun{
    background: pink;
    padding: 10px;
}
/* 这样写样式,从视觉上box节点确实是全屏覆盖了,但是有bug,
    父组件设置了transfrom或position,效果就会局限与这个父组件不会全屏展示 */
/* 为了解决这个bug,可以使用vue3内置的一个瞬移api <teleport> */
.box{
    position:fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0,0.3);
}
.msg{
    position:absolute;
    left: 0;
    margin: auto;
    top: 0;
    right: 0;
    bottom: 0;
    width: 200px;
    height: 160px;
    background: white;
}
</style>

多个Teleport共享目标

代码:
<Teleport to="#model">
    <div>A</div>
</Teleport>
<Teleport to="#model">
    <div>B</div>
</Teleport>
渲染的结果为:
<div id="model">
    <div>A</div>
    <div>B</div>
</div>

 Suspense(不确定的)

它允许我们的应用在等待异步组件时渲染一些后备内容,可以让我们创建一个平滑的用户体验。

  • 好处:在HTML模板中写异步判断,成功,等待
  • Suspense原理:通过插槽实现。一个default和一个fallback。default里面放置异步组件,fallback里面放置异步组件未渲染之前的一个样式。

<Suspense>
    具有深度异步依赖的组件
    <index/>
    
    在#fallback插槽中显示"正在加载中"
    <template #fallback>
        Loading...
    </template>
</Suspense>

 扩展:引入组件的方式有两种静态引入和动态引入,Vue3中动态引入会有变化,请看下面代码

<template>
    <div class="father">
        <h2>父组件</h2>
        <!-- <som></som> -->
        <!-- <Suspense>
        <template v-slot:default> -->
             <!-- 在使用命名插槽中,v-slot:名称和#名称,是一样的 -->
            <som></som> <!-- 如果组件内有异步请求,也会等待组件内的请求完成 -->
        <!-- </template>
        <template #fallback>
            <p>Loading...</p>
        </template>
    </Suspense> -->
    </div>
</template>

<script setup>
// 引入组件的方式有两种 静态引入和动态引入
// import som from './son.vue' //静态引入 vue3也支持此写法
//动态引入 vue2的写法
// const som = ()=>import('./son.vue')

//在vue3中不支持上面那种动态引入的写法,vue3的动态引入要从vue中导出defineAsyncComponen方法
import {defineAsyncComponent } from 'vue'
// defineAsyncComponent方法里面写一个回调函数,函数就是 ()=>import('./son.vue')
const som = defineAsyncComponent(()=>import('./son.vue'))
    // 在静态引入中,引入的代码是这个组件的一部分,会等代码全部执行完毕才会执行渲染。
    // 在动态引入中,组件的引入是异步的,
    // 如果代码程序命令较多则执行缓慢,异步引入的组件则会出现短暂的消失,
    // 为了解决这个异步组件加载未完成出现暂时缺失的情况,vue3为我们提供了一个api  <suspense>

    //<suspense>允许我们的程序在等待异步组件时渲染一些后备内容,可以让有个更平滑的体验
    //<Suspense>是通过插槽来实现的,一个是default和一个fallback,两个插槽都止允许一个子节点。
    //default里面放置异步加载的组件,fallback里面放置异步组件未渲染之前的样式
</script>
<template>
    <div class="son">
        <h6>孙组件</h6>
        <p>{{ ss }}</p>
    </div>
</template>

<script setup>
import {ref}from 'vue'

// await axios.get('') //steup简写语法里面,不需要自己写async,当出现await时会自动在setup内部出现
//     .then(date=>{console.log(date);})
//     .catch(er=>{console.log(err);})
const ss = ref('this is a async conponent')
await new Promise((resolve,reject)=>{ //等待 promise完成
    setTimeout(() => {
        resolve(ss.value='cahnge ss')
    }, 3000)
})

//注意 setup语法糖是不支持写async的,因为setup语法糖的return是返回参数,不是返回一个promise对象,使用async必须要在suspense标签内才能使用
// 前面说了使用了await 关键字 async会自动添加到setup内部,但是带有async setup()的组件必须嵌套在<suspense>中才能呈现。
</script>
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的白银时代

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值