一篇文章搞定 composition-api(番外篇)

684 阅读4分钟

前言

本文是一篇文章搞定 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 与#fallbackdefault负责显示非同步请求的元件内容,如果没有就会显示 #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 里面的错误。

小声说

点赞是不要钱的,但作者会开心好几天~