1.问题描述:
父组件异步加载获取的数据,通过props的方式传递给子组件,子组件在mounted生命周期钩子函数中,初始情况接收不到的问题
首先使用express简单模仿一个接口
// 导入express模块
const express = require('express');
// 导入cors模块
const cors = require('cors');
// 创建express的服务器实例
const app = express();
// 路由模块
const router = express.Router();
// cors跨域
app.use(cors())
// 注册路由模块
app.use('/api', router)
router.get('/list', (req, res) => {
const list = [
{name: '张三', age: 18},
{name: '李四', age: 20}
]
res.send({
code: 200,
msg: '请求成功',
data: list
})
})
// 调用app.listen方法,指定端口号并启动web服务器
app.listen(80, () => {
console.log('Express server running at http://127.0.0.1')
})
代码如下:
- 父组件
<script setup lang="ts">
import { ref, onMounted } from "vue";
import axios from "axios";
import Child from '@/components/Child.vue'
interface user {
name: string,
age: number
}
const userList = ref<user[]>([])
const getUserData = async () => {
const res = await axios.get('http://localhost/api/list')
userList.value = res.data.data
}
onMounted(() => getUserData())
</script>
<template>
<div class="container">
<p>我是父组件</p>
<Child :userList="userList"></Child>
</div>
</template>
<style scoped lang="scss">
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 600px;
height: 400px;
background: pink;
p {
text-align: center;
font-size: 28px;
font-weight: bold;
}
}
</style>
- 子组件
<script setup lang="ts">
import { onMounted } from "vue";
interface user {
name: string,
age: number
}
const props = defineProps<{
userList: user[]
}>()
onMounted(() => {
console.log(props.userList)
})
</script>
<template>
<div class="child-container">
<p>我是子组件</p>
<ul>
<li v-for="(item,index) in userList" :key="index">姓名{{ item.name }},年龄{{ item.age }}</li>
</ul>
</div>
</template>
<style scoped lang="scss">
.child-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
height: 200px;
background: gray;
p {
text-align: center;
font-size: 28px;
font-weight: bold;
}
ul {
margin-top: 20px;
}
}
</style>
在Child子组件中,我们打印了props.userList父组件异步请求获取后,传递给子组件的数据,控制台打印结果如下:
我们发现数组长度为0,并不是后端返回的数据。
2.问题产生原因
我们分别在父子组件onMounted生命周期中打印信息,查看结果:结果如下
我们发现,先打印子组件挂载,后父组件挂载。
原因解释:由于userList是异步请求获取的,需要时间。而子组件又先挂载,此时父组件userList需要一段时间才能获取到,所以打印出来的是父组件userList的默认值,即空数组。
3.解决方案
3.1使用v-if控制子组件渲染的时机
代码如下:
<Child v-if="userList && userList.length > 0" :userList="userList"></Child>
此时控制台打印结果如下:
此时父组件先挂载,然后子组件挂载。只有当父组件请求获取到异步数据之后,再开始渲染子组件,这样在子组件onMounted生命周期中就可以拿到父组件异步请求传递的数据了。
3.2子组件使用watch监听父组件传递过来的数据
代码如下:
watch(() => props.userList, (newValue, oldValue) => {
console.log(newValue,oldValue)
})
控制台打印结果如下:
因为子组件先挂载,此时父组件异步请求获取过来的数据一开始为默认值,即[],然后父组件异步请求获取到了数据,即长度为2的数组,所以打印以上内容。现在就可以在watch回调的newValue,对userList进行操作,就不会像子组件onMounted生命周期一开始是空数组的情况。
代码如下:
watch(() => props.userList, (newValue, oldValue) => {
newValue.push({name: '王五', age: 22})
})
页面展示如下:
4.问题来源
在父组件中引入子组件,子组件用于展示echarts图表,为了减少请求次数,父组件发请求,然后传递给多个子组件,进行数据展示。子组件在onMounted生命周期中进行setOptions配置,发现父组件传递的数据为空数组,子组件图表未能正确展示。当然也可以子组件分别进行网络请求,但是同一个接口会请求多次。