之后我们补充说明一些额外的函数及使用
shallowRef和shallowReactive
对响应式数据进行浅处理,即指处理一层,而不会关注深层次的变化
这一点主要是应对,如果我们有多层的对象数据,就可以使用shallowRef进行处理,减少内存消耗。
就比如我们有一个对象
{
barnd:’奔驰’,
options:{
color:’红色’,
engine:’V8′
}
}
如果使用的是reactive的话,那么无论修改哪一级的数据,都可以进行响应式的修改
但是如果是使用shallowReactive的话,尝试修改options中的数据的时候,由于超过了一级,所以并不会出现响应式效果
但是如果直接替换options的话,那么是可以生效的
同理,shallowRef也是一样的处理效果
其次是readonly和shallowReadonly
如果是readonly,使用方法如下
import { ref,reactive,readonly,shallowReadonly } from “vue”;
let sum = ref(0)
let sumonly = readonly(sum)
这样,当我们尝试修改sumonly的时候,就会进行报错,并无法生效
而对原始数据进行修改的时候,那么也会体现到sunonly之上。
shallowReadonly则是readonly的简化版,当我们有多级的对象的时候,shallowReadonly只保证第一级的对象无法被修改,我们进行一下举例
let car1 = reactive({
brand:’奔驰’,
options:{
color:’红色’,
price:100
}
})
let car2= shallowReadonly(car1)
function changeBrand(){
car2.brand= ‘宝马’
}
function changeColor(){
car2.options.color= ‘绿色’
}
function changePrice(){
car2.options.price += 100
}
对于brand的修改,由于是存在于第一级的,所以无法进行修改
但是针对options中的修改,那么就可以进行修改完成。
适合只需要保护对象顶层的场景。
toRaw和markRaw
分别用于保护响应式对象不被修改的,相当于声明了readonly
但是不一样的是,他返回的是一个原始对象
let person = reactive({name:’tony’,age:18})
// 原始对象
let rawPerson = toRaw(person)
而markRaw的话,则是标记一个对象,使其永远不会变为响应式的
最常见的就是在使用第三方类库的时候,标记一个对象,使其不会变为响应式。
let citys = markRaw([
{id:’asdda01′,name:’北京’},
{id:’asdda02′,name:’上海’},
{id:’asdda03′,name:’天津’},
{id:’asdda04′,name:’重庆’}
])
// 根据原始对象citys去创建响应式对象citys2 —— 创建失败,因为citys被markRaw标记了
let citys2 = reactive(citys)
这样在利用reactive创建对象citys2的时候,就会出现创建失败的问题
customRef,
用于自定义一个Ref标签,可以自定义对原数据的跟踪和更新的触发逻辑
其使用比较复杂
这里我们演示下首先我们需要引入这个标签
import { customRef } from “vue”;
而对于这个函数,如果希望使用它的话,需要在其中实现两个接口
get()和set()
并且搭配着两个函数进行使用
track() 和trigger()
track() 提示持续追踪,trigger() 负责进行追踪
那么综合起来,我们可以实现一个简单的ref效果,代码如下
let initValue = “hello”
let msg = customRef((track,trigger)=>{ return { // get何时调用?—— msg被读取时 get(){ track() //告诉Vue数据msg很重要,你要对msg进行持续关注,一旦msg变化就去更新 return initValue }, // set何时调用?—— msg被修改时 set(value){ initValue = value trigger() //通知Vue一下数据msg变化了 } } }) |
其使用起来和标准的ref一模一样
但是除此之外,我们还可以进行增强版的使用,比如使用customRef来要求数据在变更的一秒后才会体现在页面之上。
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);
}
}
})
Teleport
其作用是指定当前组件,需要在页面上依赖哪个父类组件,可能文字描述有点贫瘠,我们直接看下代码。
比如我们首先有一个外部的组件
<template>
<div class=”outer”>
<h2>我是App组件</h2>
<img src=”http://www.atguigu.com/images/index_new/logo.png” alt=””>
<br>
<Modal/>
</div>
</template>
在其中我们声明了使用一个子组件 Modal
在Modal之中,我们也存在着内容
但是我们希望这个类的内容可以根据页面的大小提交,而不是父类的class进行调节。在这个时候就可以使用teleport进行调节
teleport to来指定依赖于上级哪个组件
<template>
<button @click=”isShow = true”>展示弹窗</button>
<teleport to=’body’>
<div class=”modal” v-show=”isShow”>
<h2>我是弹窗的标题</h2>
<p>我是弹窗的内容</p>
<button @click=”isShow = false”>关闭弹窗</button>
</div>
</teleport>
</template>
通过teleport,来指定其依赖于body
其次是Suspense组件
如果引用了一个子组件,那么子组件很有可能会在setup的时候异步渲染一些额外内容,对于这种情况,直接引用会出现渲染问题
对此,引入了suspense,其第一可以等待渲染完成再展示子组件,第二可以在等待渲染的时候展示一些其他的内容。
比如我们有一个子类
<template>
<div class=”child”>
<h2>我是Child组件</h2>
<h3>当前求和为:{{ sum }}</h3>
</div>
</template>
<script setup lang=”ts”>
import {ref} from ‘vue’
import axios from ‘axios’
let sum = ref(0)
let {data:{content}} = await axios.get(‘https://api.uomg.com/api/rand.qinghua?format=json’)
console.log(content)
</script>
这样子组件之中存在着异步的请求,我们无法直接在父组件之中引用
如果希望在其中进行使用的话,那么可以考虑使用suspense
<template>
<div class=”app”>
<h2>我是App组件</h2>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<!– <template v-slot:fallback> –>
<!– <h2>加载中……</h2> –>
<!– </template> –>
</Suspense>
</div>
</template>
Suspense作为一个组件,类似slot的使用
内部还维护了两个不同的具名插槽
v-slot:default是加载好之后的展示
v-slot:fallback是加载中的展示
除此之外,还有一些组件
– `app.component`
– `app.config`
– `app.directive`
– `app.mount`
– `app.unmount`
– `app.use`
会在未来开发过程中用到。
例如
app.component 可以声明一个全局组件,全局都可以使用。
app.component(‘Hello’,Hello)
app.config.globalProperties.x
来声明全局的变量信息
app.directive(‘beauty’,(element,{value})=>{
element.innerText += value
element.style.color = ‘green’
element.style.backgroundColor = ‘yellow’
})
声明全局的函数