「本专栏是我在学习 Vue3 过程中的总结与分享,旨在帮助初学者快速上手 Vue3。由于我也在持续学习中,如果有任何疏漏或错误,欢迎大家在评论区指正,我们一起进步!」
提示:使用该文档学习vue3需要有一些vue和vue2的基础才可以更好的学习噢~~
版权:未经允许,禁止转载!
鼓励:也许你正处于黑暗,可别忘了,星星都是在夜幕中才闪耀光芒。自信是你内心的火种,点燃它,就能照亮前行的路,走向属于你的灿烂黎明。
————————————————
文章目录
- 前言
- 一、shallowRef 与 shallowReactive
- 二、readonly 与 shallowReadonly
- 三、toRaw 与 markRaw
- 四、customRef
- 五、Teleport
- 六、Suspense
- 总结
前言
提示:本章为Vue3学习的最后一章!!!
Vue 3 的推出带来了许多强大的新特性
,极大地提升了开发效率和用户体验。在响应式系统方面,Vue 3 提供了更细粒度的控制,例如 shallowRef、shallowReactive、readonly 等 API
,使开发者能够更灵活地管理数据状态。此外,Teleport 和 Suspense 等新特性也为组件化开发带来了更多可能性。本文将深入探讨这些 API 的使用场景和实现原理,帮助开发者更好地理解和应用 Vue 3 的新特性。
一、shallowRef 与 shallowReactive
- 作用:
shallowRef
:创建一个浅层响应的ref
,只对.value
属性进行响应式处理,不会递归追踪其内部属性的变化。shallowReactive
:创建一个浅层响应的对象,只对对象的第一层属性进行响应式处理,不会递归追踪嵌套对象的属性变化。
- 优势:
- 性能优化:避免深层递归响应式处理,减少不必要的性能开销。
- 适用于不需要深度监听的数据结构,如大型对象或外部库实例。
示例代码:
App.vue
<template>
<div class="app">
<h2>求和为:{{sum}}</h2>
<h2>名字为:{{person.name}}</h2>
<h2>年龄为:{{person.age}}</h2>
<h2>汽车为:{{car}}</h2>
<button @click="changeSum">sum + 1</button>
<button @click="changeName">修改名字</button>
<button @click="changeAge">修改年龄</button>
<button @click="changePerson">修改整个人</button>
<span>|||||||||||||||||</span>
<button @click="changeBrand">修改品牌</button>
<button @click="changeColor">修改颜色</button>
<button @click="changeEngine">修改发动机</button>
</div>
</template>
<script setup lang="ts" name="App">
import { shallowRef, ref, shallowReactive } from 'vue';
// shallowReactive
// shallowRef只处理最外层的数据
// 因此修改名字和修改年龄就不好用了
// 他们两个效果是一样的
let sum = shallowRef(0)
let person = shallowRef({
name: '张三',
age: 18
})
let car = shallowReactive({
brand: '宝马',
options:{
color: '红色',
engine: 'v8'
}
})
function changeSum(){
sum.value += 1
}
function changeName(){
person.value.name = '李四'
}
function changeAge(){
person.value.age += 1
}
function changePerson(){
person.value = {name: 'tony', age: 100}
}
function changeBrand(){
car.brand = '法拉利'
}
function changeColor(){
car.options.color = '紫色'
}
function changeEngine(){
car.options.engine = 'v12'
}
</script>
<style scoped>
.app{
background-color: #ddd;
border-radius: 10px;
padding: 10px;
}
</style>
二、readonly 与 shallowReadonly
- 作用:
readonly
:创建一个只读的响应式对象,所有嵌套属性都无法被修改。shallowReadonly
:创建一个浅层只读的响应式对象,仅第一层属性为只读,嵌套属性仍可修改。
- 优势:
- 数据保护:防止数据被意外修改,确保数据的不可变性。
- 适用于需要保护的数据场景,如全局配置或传递给子组件的 props。
示例代码:
App.vue
<template>
<div class="app">
<h2>当前sum1为:{{sum1}}</h2>
<h2>当前sum2为:{{sum2}}</h2>
<h2>当前汽车为:{{car}}</h2>
<button @click="changeSum1">点我sum1 + 1</button>
<button @click="changeSum2">点我sum2 + 1</button>
<button @click="changeBrand">修改品牌</button>
<button @click="changeColor">修改颜色</button>
<button @click="changePrice">修改价格</button>
</div>
</template>
<script setup lang="ts" name="App">
import { reactive, readonly, ref, shallowReadonly } from 'vue';
let sum1 = ref(0)
// readonly括号里面只能写响应式的数据
// sum1变化sum2也会变化,但只是因为sum1的改变而已
// reactive也是同样的效果
// shallowReadonly是只让最外层的可读,但是再里面的层次是可以正常修改
let sum2 = readonly(sum1)
let car1 = reactive({
brand: '奔驰',
options: {
color: '红色',
price: 100
}
})
let car = shallowReadonly(car1)
function changeSum1(){
sum1.value += 1
}
function changeSum2(){
sum2.value += 1
}
function changeBrand(){
car.brand = '宝马'
}
// shallowReadonly 只是最外层的数据只读噢
function changeColor(){
car.options.color = '黑色'
}
function changePrice(){
car.options.price = 300
}
</script>
<style scoped>
.app{
background-color: #ddd;
border-radius: 10px;
padding: 10px;
}
</style>
三、toRaw 与 markRaw
- 作用:
toRaw
:返回响应式对象的原始非代理对象。markRaw
:标记一个对象,使其永远不会被转换为响应式对象。
- 优势:
- 直接操作原始对象:避免响应式系统的开销,适用于性能敏感的场景。
- 防止不必要的响应式转换:适用于不需要响应式的静态数据或第三方库实例。
示例代码:
App.vue
<template>
<div class="app">
</div>
</template>
<script setup lang="ts" name="App">
import { markRaw, reactive, toRaw } from 'vue';
// toRaw可以使一个响应式的对象变成非响应式的对象
let person = reactive({
name: '张三',
age: 18
})
let rawPerson = toRaw(person)
// markRaw标记的对象是不能够再次变成响应式的数据的
let citysd = markRaw([
{id:'asdda01',name:'北京'},
{id:'asdda02',name:'上海'},
{id:'asdda03',name:'天津'},
{id:'asdda04',name:'重庆'}
])
let cityrac = reactive(citysd)
console.log('响应式对象',person);
console.log('非响应式对象',rawPerson);
console.log('非响应式对象',citysd);
console.log('非响应式对象',cityrac);
</script>
<style scoped>
.app{
background-color: #ddd;
border-radius: 10px;
padding: 10px;
}
</style>
四、customRef
- 作用:
- 创建一个自定义的
ref
,允许开发者完全控制其依赖追踪和更新触发逻辑。
- 创建一个自定义的
- 优势:
- 灵活性:可以自定义
get
和set
的逻辑,实现复杂的响应式行为。 - 适用于需要特殊逻辑的响应式数据,如防抖、异步数据加载等。
- 灵活性:可以自定义
示例代码:
useMsgRef.ts
import { customRef } from "vue";
export default function (initValue: string, delay: number) {
// track跟踪
// trigger触发
// customRef必须返回一个函数并且return一个get&set
let timer: number
let msg = customRef((track, trigger) => {
return {
get() {
track() // 告诉vue,一旦数据发生变化就必须要更新
return initValue
},
set(value) {
clearTimeout(timer)
timer = setTimeout(() => {
initValue = value
trigger() // 告诉vue 我的数据改变了
}, 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 useMsgRef from './hooks/useMsgRef';
let {msg} = useMsgRef('你好',2000)
</script>
<style scoped>
.app{
background-color: #ddd;
border-radius: 10px;
padding: 10px;
}
</style>
五、Teleport
- 作用:
- 将组件的内容渲染到 DOM 中的任意位置,而不受组件层级结构的限制。
- 优势:
- 解决 DOM 结构问题:例如将模态框、通知栏等渲染到
<body>
或其他容器中。 - 提升代码可维护性:保持逻辑组件与 DOM 结构的分离。
- 解决 DOM 结构问题:例如将模态框、通知栏等渲染到
示例代码:
Modal.vue
<template>
<button @click="isShow = true">展示弹窗</button>
<!-- 这样它就是相对整个body定位了 -->
<teleport to='body'>
<div class="modal" v-show="isShow">
<h2>我是弹窗的标题</h2>
<p>我是弹窗的内容</p>
<button @click="isShow = false">关闭弹窗</button>
</div>
</teleport>
</template>
<script setup lang='ts'>
import { ref } from 'vue';
let isShow = ref(false)
</script>
<style scoped>
.modal {
width: 200px;
height: 150px;
background-color: skyblue;
border-radius: 10px;
padding: 5px;
box-shadow: 0 0 5px;
text-align: center;
position: fixed;
left: 50%;
top: 20px;
margin-left: -100px;
}
</style>
App.vue
<template>
<div class="outer">
<h2>我是App组件</h2>
<img src="http://www.atguigu.com/images/index_new/logo.png" alt="">
<br>
<Modal></Modal>
</div>
</template>
<script setup lang="ts" name="App">
import Modal from './Modal.vue';
</script>
<style scoped>
.outer{
background-color: #ddd;
border-radius: 10px;
padding: 5px;
box-shadow: 0 0 10px;
width: 400px;
height: 400px;
filter: saturate(200%);
}
/* 因为一些例如filter的设置,或者是图片的影响 */
/* 我们的位置可能会发生改变,因此我们直接使用Teleport */
img {
width: 270px;
}
</style>
六、Suspense
- 作用:
- 提供一种优雅的方式来处理异步组件的加载状态,支持加载中和加载失败的占位内容。
- 优势:
- 提升用户体验:在异步组件加载时显示加载指示器或备用内容。
- 简化异步逻辑:统一管理异步组件的加载状态,减少代码复杂度。
示例代码:
Child.vue
<template>
<div class="child">
<h2>我是Child组件</h2>
<h3>当前求和为:{{ sum }}</h3>
</div>
</template>
<script setup lang='ts' name="Child">
import axios from 'axios';
import { ref } from 'vue';
let sum = ref(0)
// 当我们在setup中发送请求并需要返回的情况
// 如果想要正常使用就必须使用sus
let {data: {content}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 会直接给你提示,不让你在这里直接使用异步请求
console.log(content)
</script>
<style scoped>
.child {
background-color: skyblue;
border-radius: 10px;
padding: 10px;
box-shadow: 0 0 10px;
}
</style>
App.vue
<template>
<!--
我们可以直接使用 <Suspense>异步请求组件</Suspense>
当然我们也可以对其进行配置之后再使用该标签,也会产生不同的效果
-->
<div class="app">
<!-- 在这里将子组件包裹在里面,就不会出现异常 -->
<Suspense>
<!-- 它使用了卡槽 -->
<!-- default放的是子组件 -->
<template v-slot:default>
<Child></Child>
</template>
<!-- 因为需要等待请求,因此提供了一个中间的卡槽 -->
<!-- 当请求发送成功返回后就消失了 -->
<template v-slot:fallback>
<h2>加载中......</h2>
</template>
</Suspense>
</div>
</template>
<script setup lang='ts' name="App">
import { Suspense } from 'vue';
import Child from './Child.vue';
</script>
<style scoped>
.app {
background-color: #ddd;
border-radius: 10px;
padding: 10px;
box-shadow: 0 0 10px;
}
</style>
总结
今天深入探讨了 Vue 3 中的多个核心特性,包括 shallowRef
与 shallowReactive
的浅层响应式控制、readonly
与 shallowReadonly
的只读数据保护、toRaw
与 markRaw
的原始对象操作、customRef
的自定义响应式逻辑,以及 Teleport
和 Suspense
在组件渲染和异步加载中的应用。这些特性不仅优化了性能,还提升了开发灵活性和用户体验。通过合理运用这些工具,开发者可以更高效地构建高性能、可维护的 Vue 3 应用。
感谢大家对本专栏的支持,Vue3的学习到这里就结束了,很高兴能收获这么多可爱的粉丝们,我也会尽可能的多出一些前端以及后端的内容,供大家参考学习。那么…完结撒花!