一、前言:Uni-app小程序用地图组件的常见坑
很多人用Uni-app开发小程序时,只要用到地图组件就会遇到各种奇怪问题:要么地图不显示,要么定位不准,要么选点后拿不到坐标。其实这些问题大多不是组件本身的bug,而是对小程序的地图规则不熟悉,或者Uni-app的配置没弄对。这篇内容就把最常见的几个问题拆解清楚,给大家讲明白怎么解决。
二、问题1:地图组件完全不显示,一片空白
2.1 问题表现
页面里写了
2.2 原因分析
小程序的地图组件是依赖微信的地图服务的,所以必须先开通对应的权限,还要在小程序的后台配置合法域名。很多人会忘这一步,或者配置错了域名。
2.3 解决步骤
首先要在微信公众平台的小程序后台开通“位置接口”权限,路径是:设置→第三方设置→插件管理→添加插件,找到“微信位置接口”开通。然后要配置合法域名,路径是:设置→开发设置→服务器域名,把“request合法域名”“downloadFile合法域名”“uploadFile合法域名”都加上https://apis.map.qq.com(因为Uni-app的地图组件默认用的是腾讯地图)。
配置完后台,还要在Uni-app的项目里配置manifest.json文件,找到“小程序”→“权限配置”,添加位置权限的配置。
2.4 完整示例
先配置manifest.json的小程序权限,代码如下:
{
"mp-weixin": {
"permission": {
"scope.userLocation": {
"desc": "需要获取您的位置信息用于展示地图"
}
},
"requiredPrivateInfos": ["getLocation"]
}
}
然后写页面的map组件代码,确保有高度(很多人忘写高度也会导致空白):
<template>
<view class="map-container">
<!-- 地图组件,必须设置高度,否则会显示空白 -->
<map
class="map"
:longitude="longitude"
:latitude="latitude"
:scale="14"
></map>
</view>
</template>
<script>
export default {
data() {
return {
longitude: 116.397228, // 北京天安门的经度,测试用
latitude: 39.907565 // 北京天安门的纬度,测试用
}
}
}
</script>
<style>
.map-container {
width: 100%;
height: 500rpx; /* 必须设置容器高度,map组件的高度默认继承父容器 */
}
.map {
width: 100%;
height: 100%;
}
</style>
三、问题2:定位不准,或者拿不到用户真实位置
3.1 问题表现
要么定位的位置和实际位置差几公里,要么获取到的位置一直是默认的天安门,或者调用getLocation方法报错。
3.2 原因分析
第一个原因是权限配置的desc内容太短,微信要求desc必须不少于10个字,否则会被驳回;第二个原因是调用getLocation时没配置正确的参数,比如用了低精度的定位模式;第三个原因是开发时用的是微信开发者工具,工具的定位默认是北京,不是真实位置。
3.3 解决步骤
首先检查manifest.json里的desc内容,确保不少于10个字;然后调用getLocation时配置高精度模式;开发时要在微信开发者工具的“详情”→“本地设置”里把“不校验合法域名”打开,或者用真机预览测试真实定位。
3.4 完整示例
先修改manifest.json的desc,确保足够长:
{
"mp-weixin": {
"permission": {
"scope.userLocation": {
"desc": "需要获取您的位置信息用于展示周边地图和定位服务"
}
},
"requiredPrivateInfos": ["getLocation"]
}
}
然后写获取定位的代码,用高精度模式:
<template>
<view class="map-container">
<map
class="map"
:longitude="longitude"
:latitude="latitude"
:scale="14"
></map>
<button @click="getUserLocation">获取我的位置</button>
</view>
</template>
<script>
export default {
data() {
return {
longitude: 116.397228,
latitude: 39.907565
}
},
methods: {
// 获取用户真实位置
getUserLocation() {
uni.getLocation({
type: 'wgs84', // 高精度定位模式,不要用gcj02,除非是需要国测局坐标的场景
success: (res) => {
// 拿到真实的经纬度后更新地图
this.longitude = res.longitude
this.latitude = res.latitude
console.log('我的位置:', res.longitude, res.latitude)
},
fail: (err) => {
console.error('获取位置失败:', err)
// 提示用户开启位置权限
uni.showToast({
title: '请开启位置权限',
icon: 'none'
})
}
})
}
}
}
</script>
<style>
.map-container {
width: 100%;
height: 500rpx;
}
.map {
width: 100%;
height: 100%;
}
button {
margin: 20rpx;
padding: 10rpx 20rpx;
background-color: #007aff;
color: white;
border-radius: 10rpx;
}
</style>
四、问题3:选点后拿不到经纬度,或者经纬度无效
4.1 问题表现
很多小程序需要用户在地图上选点,比如外卖选收货地址、租房选位置,选完点后拿不到经纬度,或者拿到的经纬度是空的,或者用这个经纬度反查地址时出错。
4.2 原因分析
第一个原因是没给map组件绑定markers或者bindtap事件,导致选点时没有触发获取经纬度的逻辑;第二个原因是选点后拿到的经纬度是字符串类型,不是数字类型,直接用会出错;第三个原因是没处理选点时的坐标转换,比如用了wgs84坐标去反查地址,而反查接口需要gcj02坐标。
4.3 解决步骤
首先给map组件绑定bindtap事件,监听用户点击地图的行为;然后拿到经纬度后转成数字类型;如果需要反查地址,要把wgs84坐标转成gcj02坐标。
4.4 完整示例
先写选点的代码,包括坐标转换:
<template>
<view class="map-container">
<map
class="map"
:longitude="longitude"
:latitude="latitude"
:scale="14"
@tap="onMapTap" <!-- 绑定地图点击事件 -->
:markers="markers" <!-- 显示选点的标记 -->
></map>
<view class="tip">点击地图选点</view>
<view class="result" v-if="selectedLocation">
选点经度:{{ selectedLocation.longitude }}<br>
选点纬度:{{ selectedLocation.latitude }}
</view>
</view>
</template>
<script>
export default {
data() {
return {
longitude: 116.397228,
latitude: 39.907565,
markers: [], // 选点的标记数组
selectedLocation: null // 选点的位置信息
}
},
methods: {
// 地图点击事件,用户选点
onMapTap(e) {
// 拿到点击位置的经纬度,注意是字符串类型,需要转成数字
const longitude = Number(e.detail.longitude)
const latitude = Number(e.detail.latitude)
// 更新选点的标记,显示在地图上
this.markers = [{
id: 1,
longitude: longitude,
latitude: latitude,
iconPath: '/static/marker.png', // 选点的图标,需要自己准备
width: 30,
height: 30
}]
// 保存选点的位置信息
this.selectedLocation = {
longitude: longitude,
latitude: latitude
}
// 如果需要反查地址,把wgs84转成gcj02坐标
this.convertToGcj02(longitude, latitude)
},
// 坐标转换:wgs84转gcj02
convertToGcj02(wgsLon, wgsLat) {
// 调用腾讯地图的坐标转换接口,需要申请key
uni.request({
url: 'https://apis.map.qq.com/ws/coord/v1/translate',
method: 'GET',
data: {
locations: `${wgsLat},${wgsLon}`, // 注意接口要求的格式是纬度,经度
type: 1, // 1表示wgs84转gcj02
key: '你的腾讯地图key' // 替换成自己申请的key
},
success: (res) => {
if (res.data.status === 0) {
const gcjLocation = res.data.locations[0]
console.log('转换后的gcj02坐标:', gcjLocation)
// 可以用这个坐标去反查地址
this.reverseGeocode(gcjLocation.lng, gcjLocation.lat)
}
}
})
},
// 反查地址:根据经纬度获取详细地址
reverseGeocode(lon, lat) {
uni.request({
url: 'https://apis.map.qq.com/ws/geocoder/v1/',
method: 'GET',
data: {
location: `${lat},${lon}`, // 纬度,经度
key: '你的腾讯地图key' // 替换成自己申请的key
},
success: (res) => {
if (res.data.status === 0) {
const address = res.data.result.address
console.log('选点的详细地址:', address)
uni.showToast({
title: '选点成功:' + address,
icon: 'none',
duration: 3000
})
}
}
})
}
}
}
</script>
<style>
.map-container {
width: 100%;
height: 500rpx;
}
.map {
width: 100%;
height: 100%;
}
.tip {
text-align: center;
margin: 20rpx;
color: #666;
}
.result {
margin: 20rpx;
padding: 20rpx;
border: 1rpx solid #eee;
border-radius: 10rpx;
}
</style>
五、问题4:地图组件的层级问题,被其他元素遮挡
5.1 问题表现
地图组件上面要放一个搜索框或者按钮,结果地图组件把搜索框挡住了,或者搜索框放在地图上面却显示不出来。
5.2 原因分析
小程序的原生组件(包括map、video、canvas等)的层级是最高的,会覆盖所有普通的view、text等元素,所以普通元素放在地图上面会被挡住。
5.3 解决步骤
第一种方法是用cover-view、cover-text这些专门用来放在原生组件上面的标签;第二种方法是把地图组件和普通元素分开,比如普通元素放在地图的下面,不要重叠;第三种方法是给地图组件设置z-index为-1,给普通元素设置更高的z-index,但这种方法在有些平台可能不生效。
5.4 完整示例
用cover-view放在地图上面的代码:
<template>
<view class="map-container">
<map
class="map"
:longitude="longitude"
:latitude="latitude"
:scale="14"
></map>
<!-- 用cover-view放在地图上面,不会被遮挡 -->
<cover-view class="search-box">
<cover-input type="text" placeholder="搜索地点" class="search-input"></cover-input>
<cover-view class="search-btn">搜索</cover-view>
</cover-view>
</view>
</template>
<script>
export default {
data() {
return {
longitude: 116.397228,
latitude: 39.907565
}
}
}
</script>
<style>
.map-container {
width: 100%;
height: 500rpx;
position: relative; /* 父容器设置相对定位,方便子元素绝对定位 */
}
.map {
width: 100%;
height: 100%;
}
/* cover-view必须设置绝对定位,否则不会显示在地图上面 */
.search-box {
position: absolute;
top: 20rpx;
left: 20rpx;
right: 20rpx;
display: flex;
align-items: center;
background-color: white;
padding: 10rpx;
border-radius: 10rpx;
}
.search-input {
flex: 1;
padding: 10rpx;
border: none;
outline: none;
}
.search-btn {
padding: 10rpx 20rpx;
background-color: #007aff;
color: white;
border-radius: 10rpx;
}
</style>
六、应用场景、优缺点、注意事项
6.1 应用场景
Uni-app的地图组件在小程序里的应用场景非常广,比如外卖小程序的收货地址选择、打车小程序的司机定位、旅游小程序的景点展示、房产小程序的房源位置展示、校园小程序的校园导航等等,只要需要用到位置信息和地图展示的场景,都可以用这个组件。
6.2 技术优缺点
优点:首先是跨平台,用Uni-app写一次代码,可以同时发布到微信、支付宝、百度等多个小程序平台,不用为每个平台单独写地图代码;其次是开发成本低,不用自己从零开发地图服务,直接用组件就能实现基础的地图展示、定位、选点功能;最后是维护方便,只要组件有更新,不用修改自己的代码就能获得新的功能。 缺点:首先是依赖小程序平台的地图服务,比如微信小程序只能用微信的地图服务,不能随便换其他地图服务;其次是原生组件的层级问题,处理起来比较麻烦;最后是定制化程度有限,比如要实现一些特殊的地图功能,比如热力图、轨迹回放,可能需要自己集成第三方的地图SDK,增加开发难度。
6.3 注意事项
第一,一定要配置好小程序后台的权限和合法域名,否则地图组件会无法正常工作;第二,获取定位时一定要用高精度模式,并且处理好权限被拒绝的情况,给用户友好的提示;第三,选点后拿到的经纬度要转成数字类型,并且注意坐标的类型,需要反查地址时要转成gcj02坐标;第四,地图组件上面的元素要用cover-view,避免被遮挡;第五,开发时尽量用真机测试,因为微信开发者工具的定位是模拟的,和真实环境有差异。
七、文章总结
Uni-app在小程序中使用地图组件的常见问题,大多是因为对小程序的规则不熟悉,或者配置、代码的细节没处理好。只要按照上面的步骤,把权限配置好,代码的细节处理到位,就能解决大部分问题。如果遇到更复杂的问题,可以去Uni-app的官方文档或者社区找解决方案,也可以自己集成第三方的地图SDK来实现更复杂的功能。
评论
围绕“解决Uni-app在小程序中使用地图组件的常见问题”参与讨论