《uni-app开发Harmony Next平台的App》第三篇:内置模块之地图与定位——集成腾讯地图
鸿蒙开发里,地图和定位是高频能力,几乎每个需要位置服务的App都绕不开。但很多人第一次接触这个能力时,会发现官方示例看起来很简单,但实际项目里跑起来并不顺利——地图加载不出来、定位始终返回错误码、页面跳转后经纬度全丢。这个功能本身不复杂,真正难的是配置和调试环节。uni-app的跨平台特性虽然屏蔽了大量底层差异,但HarmonyOS NEXT的开发模式与Android/iOS有本质不同,地图组件通
《uni-app开发Harmony Next平台的App》第三篇:内置模块之地图与定位——集成腾讯地图

地图和定位,在鸿蒙上绕不开的坎
鸿蒙开发里,地图和定位是高频能力,几乎每个需要位置服务的App都绕不开。但很多人第一次接触这个能力时,会发现官方示例看起来很简单,但实际项目里跑起来并不顺利——地图加载不出来、定位始终返回错误码、页面跳转后经纬度全丢。
这个功能本身不复杂,真正难的是配置和调试环节。uni-app的跨平台特性虽然屏蔽了大量底层差异,但HarmonyOS NEXT的开发模式与Android/iOS有本质不同,地图组件通过WebView加载,协议也不是常见的http,这导致了很多开发者卡在配置环节。
下面直接展开讲,怎么在uni-app项目里把腾讯地图和定位功能跑通。
环境说明
HBuilderX 版本:HBuilderX 4.31 及以上
目标设备:HarmonyOS NEXT 真机(模拟器部分功能受限,建议真机测试)
地图服务:腾讯地图(当前HarmonyOS平台唯一支持的地图服务)
先搞懂它在解决什么问题
涉及哪些能力
| 功能 | 对应API/组件 | 适用场景 | 是否必须联网 |
|---|---|---|---|
| 展示地图 | <map> 组件 |
位置展示、轨迹回放、区域标记 | 是 |
| 获取当前位置 | uni.getLocation() |
定位签到、附近搜索、获取经纬度 | 是 |
| 选择地点 | uni.chooseLocation() |
地址选择、收货地址填写 | 是 |
| 打开地图导航 | uni.openLocation() |
跳转到目的地、路线规划 | 是 |
为什么不支持高德或其他地图
这是HarmonyOS生态的一个现实限制。目前uni-app在鸿蒙平台只内置了腾讯地图的适配,高德等其他厂商还没有推出鸿蒙原生SDK。如果你想在鸿蒙上用高德的地图能力,那就得自己用Naitve API去对接鸿蒙的Map Kit,跟uni-app内置模块是两个路径。
配置腾讯地图Key
第一步:申请腾讯地图开发者Key
先去腾讯位置服务官网注册应用,申请Key。这一步跟Android/iOS上申请的Key不同之处在于:
- HarmonyOS NEXT的页面加载使用的是
file://和https://混合协议 - 域名白名单必须留空,否则地图组件无法正常加载
第二步:配置manifest.json
如果你是HBuilderX 4.31及以上版本,直接在可视化界面配置:

如果你习惯源码方式编辑,打开manifest.json,在app-plus -> distribute -> sdkConfigs下添加:
{
"app-harmony": {
"distribute": {
"sdkConfigs": {
"maps": {
"qqmap": {
"key": "你的腾讯地图Key"
}
}
}
}
}
}
这里有个容易踩的坑:如果大版本不对,比如你用HBuilderX 4.26到4.30之间的版本,配置方式会不一样,必须按上面的方式手动添加。4.31之后直接在可视化界面操作就行。
第三步:配置HarmonyOS权限
在harmony-configs/entry/src/main/module.json5里添加定位权限声明:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "用于获取模糊位置"
},
{
"name": "ohos.permission.LOCATION",
"reason": "用于获取精确位置",
"usedScene": {
"abilities": ["MainAbility"],
"when": "foreground"
}
},
{
"name": "ohos.permission.LOCATION_IN_BACKGROUND",
"reason": "后台定位需求(可选)",
"usedScene": {
"abilities": ["MainAbility"],
"when": "always"
}
}
]
}
}
权限这一块跟Android很相似,鸿蒙也分精确位置和模糊位置。如果不申请精确位置权限,uni.getLocation的altitude(海拔)数据会拿不到。
核心实现:完整页面示例
下面这个页面包含了三个核心功能:地图展示、获取当前位置、选择地点。
<!-- pages/map-demo.vue -->
<template>
<view class="container">
<!-- 地图组件 -->
<map
class="map"
:longitude="centerLon"
:latitude="centerLat"
:markers="markers"
:scale="scale"
show-location
@markertap="onMarkerTap"
/>
<!-- 操作按钮区域 -->
<view class="buttons">
<button type="primary" @click="getMyLocation">获取当前位置</button>
<button type="default" @click="openLocation">打开地图导航</button>
<button type="warn" @click="chooseLocation">选择地点</button>
</view>
<!-- 位置信息展示 -->
<view class="info" v-if="locationInfo">
<text>经度:{{locationInfo.longitude}}</text>
<text>纬度:{{locationInfo.latitude}}</text>
<text v-if="locationInfo.name">地点名称:{{locationInfo.name}}</text>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
// 地图中心点(默认北京天安门)
const centerLat = ref(39.90923)
const centerLon = ref(116.397428)
const scale = ref(16)
// 标记点
const markers = ref([
{
id: 1,
latitude: 39.90923,
longitude: 116.397428,
iconPath: '/static/marker.png',
width: 30,
height: 30,
title: '天安门',
label: {
content: '出发地点',
color: '#333',
fontSize: 14
}
}
])
// 位置信息
const locationInfo = ref(null)
/**
* 获取当前位置 - 核心方法
*
* 注意:这里没有使用 type: 'gcj02' 会导致坐标偏移
* 腾讯地图使用 gcj02 坐标系,不指定的话返回原始坐标
*/
const getMyLocation = () => {
uni.getLocation({
type: 'gcj02',
isHighAccuracy: true,
highAccuracyTimeout: 3000,
success: (res) => {
console.log('定位成功', JSON.stringify(res))
// 更新地图中心到定位位置
centerLat.value = res.latitude
centerLon.value = res.longitude
// 更新标记点到当前位置
markers.value = [{
id: new Date().getTime(),
latitude: res.latitude,
longitude: res.longitude,
iconPath: '/static/marker.png',
width: 30,
height: 30,
title: '当前位置'
}]
locationInfo.value = res
},
fail: (err) => {
console.error('定位失败', JSON.stringify(err))
uni.showToast({
title: '定位失败,请检查权限',
icon: 'none'
})
}
})
}
/**
* 打开地图导航 - 使用uni.openLocation
*
* 这个API会调起系统的导航页面
* 在鸿蒙上是一个WebView加载的腾讯地图页面
*/
const openLocation = () => {
if (!locationInfo.value) {
uni.showToast({
title: '请先获取当前位置',
icon: 'none'
})
return
}
uni.openLocation({
latitude: locationInfo.value.latitude,
longitude: locationInfo.value.longitude,
name: locationInfo.value.name || '我的位置',
address: locationInfo.value.address || '',
scale: 18,
success: () => {
console.log('打开导航成功')
},
fail: (err) => {
console.error('打开导航失败', JSON.stringify(err))
}
})
}
/**
* 选择地点 - 使用uni.chooseLocation
*
* 返回选择的经纬度和地址信息
* 适合用来做:收货地址选择、活动地点选择
*/
const chooseLocation = () => {
uni.chooseLocation({
latitude: centerLat.value,
longitude: centerLon.value,
keyword: '', // 可选,搜索关键词
success: (res) => {
console.log('选择地点成功', JSON.stringify(res))
// 更新地图中心到选择的地点
centerLat.value = res.latitude
centerLon.value = res.longitude
// 添加新标记点
markers.value = [{
id: new Date().getTime(),
latitude: res.latitude,
longitude: res.longitude,
iconPath: '/static/marker.png',
width: 30,
height: 30,
title: res.name
}]
locationInfo.value = res
},
fail: (err) => {
console.error('选择地点失败', JSON.stringify(err))
}
})
}
/**
* 点击标记点事件
*/
const onMarkerTap = (e) => {
console.log('点击了标记点', JSON.stringify(e))
uni.showToast({
title: `标记点:${e.markerId}`,
icon: 'none'
})
}
</script>
<style>
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.map {
flex: 1;
width: 100%;
}
.buttons {
display: flex;
flex-direction: column;
padding: 20rpx;
gap: 12rpx;
}
.info {
padding: 20rpx;
background: #f5f5f5;
font-size: 28rpx;
line-height: 1.8;
}
</style>
核心代码解析
getMyLocation函数:
- 必须传递
type: 'gcj02'参数。不传的话返回的是WGS84坐标系,在腾讯地图上会偏移几百米。这一点很容易被忽略。 isHighAccuracy: true启动高精度模式,通过GPS和基站辅助定位,定位速度更快。- 定位成功后会更新地图中心点和标记点,让用户看到自己的位置。
openLocation函数:
- 依赖之前定位或选择的地点数据。如果没获取到位置直接调用,会跳转到空白页面。
- 在鸿蒙上,这个API底层也是通过WebView加载腾讯地图的导航页面,返回按钮不太一样,需要用户手动点击返回。
chooseLocation函数:
- 可以传入经纬度作为初始位置(用于定位到当前位置附近)。
keyword参数可以用来搜索特定地点,比如传“医院”会筛选出附近的医院。- 返回的数据包含
name(名称)、address(地址)、latitude/longitude(经纬度)。
调试时的域名白名单配置
这个问题是很多人在真机调试时遇到的:地图一片空白,控制台也没有任何错误。原因在于:
HarmonyOS NEXT上的页面目前不是通过http/https加载的,uni-app在鸿蒙上使用了一种特殊的本地协议。腾讯地图的JavaScript SDK默认检查当前页面的域名,如果不是白名单内的域名,就会拒绝加载地图。
解决方案:
在腾讯位置服务控制台的Key管理中,把域名白名单留空。这意味着这个Key可以在任何域名下使用,虽然不太安全,但在当前鸿蒙这类特殊环境下是唯一可行的方案。

后续uni-app官方表示会调整成以http方式加载页面,届时就可以像Android和iOS一样正常配置域名白名单了。
常见问题
问题1:地图一直显示“正在加载”,无法渲染
现象:页面能打开,但地图区域一直显示灰色或加载中。
原因:
- Key配置不生效(manifest.json写错位置)。
- 域名白名单未正确配置。
- 腾讯地图服务端域名未在HarmonyOS网络配置中允许。
解决方案:
- 检查manifest.json中
maps.qqmap.key是否在正确的层级。 - 确认腾讯地图控制台的域名白名单为空。
- 在HarmonyOS的网络配置中,允许
*.map.qq.com和*.lbs.qq.com等域名(通常不需要额外配置,但如果企业网络环境有防火墙限制则需要)。
问题2:getLocation返回错误码,定位失败
现象:点击获取位置按钮,fail回调执行。
原因:
- 权限未授予。
- 定位模块初始化失败(鸿蒙的定位服务需要LiteOS级别支持)。
- 定位超时(默认超时时间较短)。
解决方案:
- 检查
module.json5中是否声明了ohos.permission.LOCATION。 - 在手机设置中确认应用定位权限已打开(鸿蒙的权限管理比Android更严格)。
- 增加
highAccuracyTimeout参数到5秒以上,有些设备首次定位需要更长时间。
问题3:chooseLocation选择地点后返回空数据
现象:调起选择页面后,选择了一个地点,但返回的数据是空的。
原因:
- 页面生命周期问题:返回数据时页面已经被销毁。
- 回调函数未正确绑定。
- 鸿蒙WebView的postMessage机制存在延迟。
解决方案:
- 在
success回调中立即保存数据到本地存储(uni.setStorageSync),避免页面销毁丢失数据。 - 不要在该回调中执行耗时操作,否则返回数据可能被“丢弃”。
- 考虑使用
Promise封装:
const chooseLocationAsync = () => {
return new Promise((resolve, reject) => {
uni.chooseLocation({
success: resolve,
fail: reject
})
})
}
// 使用
const location = await chooseLocationAsync()
最佳实践
1. 定位请求加防抖,避免连续触发
用户如果快速点击“获取位置”按钮两次,两次请求会并行执行,可能导致定位模块异常。推荐:
let isLocating = false
const getMyLocation = () => {
if (isLocating) {
return
}
isLocating = true
uni.getLocation({
// ...
complete: () => {
isLocating = false
}
})
}
2. 每次定位都更新地图标记点
如果不更新标记点,用户定位成功后地图上还是之前的标记点,视觉上会让人困惑。就像上面示例代码里的处理。
3. 使用高精度模式但设置合理超时
isHighAccuracy: true结合highAccuracyTimeout: 5000是推荐组合。太短(如1000ms)导致频繁超时;太长(如10000ms)用户体验差。
FAQ
Q:为什么真机定位正常,模拟器上getLocation一直失败?
模拟器通常没有真实的GPS模块,也没有基站辅助定位。鸿蒙模拟器对位置模拟的支持还不太完善。建议所有定位相关功能都在真机上验证。
Q:页面返回后,之前选择的地点信息丢了,怎么处理?
这是页实例销毁后状态丢失的问题。建议在chooseLocation成功回调中把数据存到uni.setStorageSync或者全局状态管理(如Pinia/Vuex)中,返回页面时再从存储中读取。
// 选择地点时
uni.setStorageSync('selectedLocation', res)
// 页面onShow时
onShow(() => {
const savedLocation = uni.getStorageSync('selectedLocation')
if (savedLocation) {
centerLat.value = savedLocation.latitude
// ...其他处理
}
})
Q:为什么需要设置type为gcj02?不设置会怎样?
不设置type或设置为wgs84时,返回的是GPS原始坐标。腾讯地图使用gcj02坐标系统,偏移量可能达到几百米,在地图上标记点会明显不在实际位置上。Android上也有这个区别,但很多应用会自动转换,鸿蒙上需要开发者手动处理。
示例代码地址:GitHub 项目地址(包含完整配置和演示页面)
如果你也遇到类似问题,可以重点检查Key配置和权限声明这两个环节。官方文档对这部分行为描述得比较简单,但实际开发中配置出错是最高频的问题。
更多推荐



所有评论(0)