如何遇到这个问题的

在写一个地图业务的时候,需要点击地图右下角的放大框,然后以弹窗形式将地图放大,这里用的是Element-Plus中的Dialog组件。大概的结构是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 父组件 -->
<div>
<Amap></Amap>
<MapDialg></MapDialg>
</div>

<!-- 弹窗组件 -->
<div>
<el-dialog
:z-index="2000"
v-model="dialogVisible"
title="位置"
width="1000"
:before-close="handleClose"
>
<div style="width: 100%; height: 500px">
<AMap ref="mapRef"></AMap>
</el-dialog>
</div>

可以看出其实就是一个地图的嵌套操作。接下来遇到什么问题呢,就是当我们点击放大地图的时候,就是将原来父组件地图上的经纬度坐标放到我们弹窗组件上,这里我们用了hooks来减少编码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const initMap = () => {
mapLoading.value = false;
if (location.value.latitude && location.value.longitude) {
mapDivRef.value.addPlaceMarker(
location.value.longitude,
location.value.latitude,
17
);
}
};

const handleOpen = data => {
location.value = data.value;
initMap();
dialogOpen(); //dialog hoosk的方法
};

const handleClose = () => {
dialogClose(); //dialog hoosk的方法
};

defineExpose({ handleOpen });

我们借助vue3的defineExpose将弹窗组件的handleOpen方法向外抛出给父组件用,正常来说父组件如果点击就会触发handleOpen接着触发initMap()方法将地图初始化。但是事实上,我们无法获取到mapDivRef.value所以没把法调用Amap组件身上的方法初始化地图。

解决方法

其实我们知道Vue如果有组件嵌套,也就是有父子组件的时候是这样的执行顺序

父组件 beforeCreated => 父组件 created => 父组件 beforeMounted
=>子组件 beforeCreated => 子组件 created => 子组件 beforeMounted => 子组件 mounted =>父组件 mounted

其实我们需要拿到ref也就是组件本身必须得在beforeMounted才能拿到真实的dom,而我们进入的弹窗组件时候可能还没加载完毕,也就是vue身上还没拿到真实dom所以获取不到mapDivRef。这时候其实也很容易,我们只需要给init方法改为异步就行了。最简单的就是加一个定时器。

1
2
3
4
5
6
7
const handleOpen = data => {
location.value = data.value;
setTimeout(() => {
initMap();
}, 100);
dialogOpen();
};

这样就能解决我们无法获取到mapDivRef的问题,我们也就顺利解决了这个问题。

总结

vue的生命周期还是需要牢记,因为有时候出现的一些bug就会因为生命周期不够熟悉而导致!!