前言
本文是一篇文章搞定 composition-api的番外篇,填坑,内容包括:
- 用 reactive 还是 ref
- v-if & v-for 他俩不能在一起啊
- 生命周期 hooks 与 async/await
- Suspense 非同步元件
用 reactive 还是 ref
主要还是需要依照资料状态来判断使用:
假如今天单纯要定义一个基本型别的响应资料,所以很简单的我们可以使用接受任何型态资料的 ref () 来包装,再透过.value 来读取值。
const count = ref(0);
const isShow = ref(false);
但如果资料的型别为物件或是阵列的时候
const person1 = ref({
name: 'winnie'
})
const person2 = reactive({
name: '大侠爱吃汉堡饱'
})
onMounted(()=>{
console.log(person1.value.name) //ref()
console.log(person2.name) //reactive()
})
//我们可以看到在语法上 ref 需要透过.value 来取物件中的值,而在写法上就需要用到三层了,而 reactive 只需用物件取值的方式,所以在写法只需要两层。
我们可以看到在语法上 ref
需要透过.value
来取物件中的值,而在写法上就需要用到三层了,而 reactive 只需用物件取值的方式,所以在写法只需要两层
另外我们再来看看以下另一个ref与reactive对比范例:
// App.vue
const name = ref('winnie')
const age = ref(24)
const address = ref('桃园')
const setName = ()=>{
name.value = '大侠爱吃汉堡饱'
}
provide('name',name)
provide('age',age)
provide('address',address)
// 略..
// user.vue
const name=inject('name')
const address=inject('address')
const age=inject('age')
// 略..
从上我们可以看到当有一笔 user 资料要 provide 给多个子元件引用时, 如果我们透过 ref()
一一来定义,好像会显得元件中的相关资料太分散了,而且在子元件也要呼应来定义,当程式码一多也就容易导致错误,易读性变低。而如果我们改成 reactive 来进行包装的话就可以把相关资料定义在物件之中,在 provide 出去,不仅很方便,同时还可以减少资料分散的问题。
范例如下:
const userData = reactive({
name :'winnie',
age :24,
address :'桃园',
})
provide('user',userData);
总结以上,针对两个响应式资料定义的方法中,以我个人选择,
- 在单纯资料状态使用上我会使用 ref 来进行响应式资料包装,
- 而如果是物件或阵列的型别,我多半会使用 reactive 来进行包装,
总归一样还是要看当下资料状态关联来选择你要如何使用。
v-if 与 v-for 为何不建议一起使用
优先权顺序
在官方文件中有提到当 Vue3 在处理指令时,v-if 会比 v-for 具有更高的优先处理权,所以 v-if 将没有权限可以访问 v-for 里的值。
而再回到刚刚范例中,大家应该就可以清楚知道'status' 为 undefined 的原因是在v-if是被执行时,squares 中的 status 还不存在,所以v-if执行时会找不到值进而出现错误,让你改都改不了。
如果遇到必要一起使用的情况, 在官方建议可以透过computed与template来进行处理,使用如下:
computed
通过计算数性来筛选每个值,回传给模板。
const setSquaresState = computed(()=>{
squares.filter(item => item.status)
})
return { setSquaresState }
template
另外,也可以通过 <template>
标签将元件包起来,达成元件渲染。
<template v-for='item in squares' :key="item" >
<div class="squ" v-if="item.status" :style="item.style" >
{{item.id}}
</div>
</template>
生命周期中使用 async/await 的问题
错误写法:
import { ref, onBeforeMount } from 'vue';
//略...
setup(){
const newsData = ref([]);
const fetchData = async(url,datas) => {
const res = await fetch(url).then(res => res.json());
await res.map( data => datas.push(data));
}
onBeforeMount(() => {
fetchData(url, newsData.value);
})
//略...
}
在生命周期 hooks 里是采取同步执行的,所以在生命周期 hooks 中写 async/await 也无太大意义,因为生命周期 hooks 并不会等到 await 执行完,才执行下一个周期的 hooks。
另外尤雨溪大大也提到生命周期 hooks 是不支持 async 的,那要使用异步怎么办?
Suspense 非同步元件出场!
Suspense 非同步元件
Suspense 是 Vue3 新增的 API,其主要作用是让我们在进行非同步资料载入时,可以先提供初始内容来显示,当非同步加载完后在显示其请求的内容,做到简化非同步请求 UI 的处理逻辑。
下方为一个负责处理 API 的非同步元件,透过 fetch 的方式来取得 API 资料回传给 template 使用。
// Picture.vue 子元件
<template>
<div class="pic">
<img :src="data.src" alt="">
</div>
</template>
<script>
const requestUrl = `../../public/Pic.json`;
export default {
async setup() { //此时在此元件中可以使用 setup 加上 async 。
const response = await fetch(requestUrl)
.then(res => res.json())
.then(data => data)
return{
data :response[0]
}
}
}
</script>
接着我们在上层元件中的 template 新增一个 Suspense,而在其之中会有两个元件被所包覆,分别是 default
与#fallback
,
default
负责显示非同步请求的元件内容,如果没有就会显示 #fallback
元件内容。范例如下
// App.vue元件
<template>
略..
<Suspense>
<template #fallback> //如果 非同步未完成会显示 #fallback 内容
<Loading/>
</template>
<template #default>
<Picture/>
</template>
</Suspense>
</template>
略..
而这边需注意,在官方文件有提到元件主要提供了另一个方案,允许将等待过程提升到元件树中来处理,而不是在单一元件之中。
也就是说如果我们在爸爸阶层那辈来使用async setup()
画面还是会坏掉的。
预览:
ps. 我的小新好可爱喔
遇到 API 错误了怎么办?
当 <template #default>
中内部的 API 发生错误时,我们可以透过 onErrorCaptured
生命周期 hooks 来找出 Suspense 里面的错误。
小声说
点赞是不要钱的,但作者会开心好几天~