有的时候我们想要
从服务端拿到数据后
再去渲染一个组件,为了实现这个效果我们目前有几种实现方式:
-
将数据请求放到父组件去做,并且使用
v-if
控制拿到子组件后才去渲染子组件,然后将数据从父组件通过
props
传给子组件。
-
在子组件的
onMounted
中请求数据,并且使用
v-if
在子组件的
template
最外层进行控制,只有拿到数据后才渲染子组件中的内容。
上面这两种方案都有各自的缺点,不够完美。最理想的方案是将从服务端获取数据的逻辑放在子组件中,并且在获取数据的期间让子组件
“暂停”
一下,先不去渲染,等到数据请求完成后再第一次去渲染子组件。
完美的解决方案
第一种方法的缺点是:子组件虽然拿到数据后才开始渲染,但是数据请求的逻辑却放到了父组件上面,我们期望所有的逻辑都封装在子组件内部。
第二种方法的缺点是:实际上是初始化时就渲染了一次子组件,此时我们还没从服务端拿到数据。所以不得不使用
v-if
在
template
的最外层控制,此时不渲染子组件中的内容。当从服务端拿到数据后再第二次渲染子组件,此时才将子组件中的内容渲染到页面上。
这种方法明显子组件渲染了2次。
那么有没有一种完美的方案,从服务端获取数据的逻辑放在子组件中,并且在获取数据的期间让子组件
“暂停”
一下,先不去渲染,等到数据请求完成后再第一次去渲染子组件呢?
答案是:当然可以,vue3的
Suspense组件
+
在setup顶层使用await获取数据
就能完美的实现这个需求!!!
两个不完美的例子
为了让你更直观的看到完美方案的牛逼,我们先来看看前面讲的两个不够完美的例子。
父组件中请求数据的例子
下面这个是父组件中请求数据的例子,父组件的代码如下:
<ChildDemo v-if="user" :user="user" />
<div v-else>
<p>loading...p>
div>
</template>
子组件的代码如下:
<div>
<p>用户名:{{ user.name }}p>
<p>手机号:{{ user.phone }}p>
div>
</template>
<ChildDemo />
</template>
子组件代码如下:
<div v-if="user">
<p>用户名:{{ user.name }}p>
<p>手机号:{{ user.phone }}p>
div>
<div v-else>
<p>loading...p>
div>
</template>
完美方案的父组件
下面这个是使用Suspense
改造后的父组件代码,如下:
<Suspense>
<AsyncChildDemo />
<template #fallback>loading...template>
Suspense>
</template>
在父组件中使用了
Suspense
组件,给这个组件传了2个插槽。
#default
插槽为异步子组件
AsyncChildDemo
,默认插槽可以不用给元素上面添加
#default
。
并且使用了
#fallback
插槽,在异步子组件加载过程中会暂时先不去渲染异步子组件
AsyncChildDemo
。改为先渲染
#fallback
插槽中的loading,等到异步子组件加载完成后会自动将loading替换为子组件中的内容。
完美方案的子组件
下面这个是使用了
await
改造后的子组件代码,如下:
<div>
<p>用户名:{{ user.name }}p>
<p>手机号:{{ user.phone }}p>
div>
</template>