uni-app跨端地图实战:用第三方LBS替代微信平台收费服务

概述

随着头部地图平台纷纷收费,5 万一年的入场券额外增加了中小型企业的负担。最近在开发中遇到微信小程序关于地图更换的问题。

我们发现,微信小程序目前仅支持自家的 <map> 组件,使用它展示默认样式地图------底图加载、缩放、拖拽、markers 标记、polyline 路线、circle 圆形区域、显示用户当前定位红点、自定义 marker 图标、文字 label------这些基础渲染功能是不收费的 。真正收费的是逆地址解析POI 搜索等 LBS 数据服务。如果已经购买了其他家 LBS 服务,再为平台的 API 额外付费显然不划算。

周末琢磨了一下,在 uni-app 平台上,完全可以把 map 组件只当作渲染层,LBS 数据服务全部走已经购买的服务商 API。

本文记录基于我们已购买的维智定位服务,实现的一套代码跑通 App、微信小程序、H5 三端的完整过程。

环境

  • 框架:uni-app(Vue 2)
  • 地图渲染 :App 端 / H5 端使用维智Web地图,小程序端使用微信原生 <map>
  • 定位 & LBS 服务:维智定位 SDK(逆地理编码、正地理编码、POI 搜索、附近搜索)

核心思路:地图是壳,LBS 是内核

arduino 复制代码
┌──────────────────────────────────────┐
│            index.vue                  │
│  按钮定义、业务逻辑(三端共用)          │
└──────────┬───────────────────────────┘
           │
   ┌───────┼───────┐
   ▼       ▼       ▼
 App端  小程序    H5
   │       │       │
   │   <map>   aimap-gl   ← 只负责渲染(免费的)
   │   组件     SDK
   │
   └── 维智 SDK(逆地址/Poi/定位) ← LBS 数据(已购买)

关键认知:map 组件只是一个"画布",你在上面画 markers、画路线,它不收费。收费的是"这个坐标对应什么地址"、"搜索附近的餐厅"这类数据查询。把数据查询换成自己买的 LBS 供应商即可。

实现过程

1. 地图控制器抽象层

三端的地图操作方式完全不同------App 端通过 web-view 内的 evalJS 调用,小程序通过 uni-app 数据绑定,H5 直接操作 SDK 实例。如果不加抽象,业务代码里会充满条件编译。

我们设计一个统一的 MapController 接口:

js 复制代码
// utils/map.js
export function createMapController(platform, options) {
  switch (platform) {
    case 'app-plus':  return new AppMapController(options);
    case 'mp-weixin': return new MiniMapController(options);
    case 'h5':        return new H5MapController(options);
  }
}

三端实现同一套 API:

方法 App端 小程序端 H5端
getCenter() evalJS → postMessage 读 data 属性 map.getCenter()
flyTo(center, zoom) evalJS 修改绑定数据 map.flyTo()
addMarker(point) evalJS push 到 markers 数组 直接调用 SDK
setStyle(style) evalJS 不支持 map.setStyle()

这样业务代码只跟 mapController 打交道,不关心平台差异。

2. App 端踩坑:web-view 原生层级问题

这是整个开发中耗时最长的一个坑。

现象:App 端(Android)上,所有 Vue 层写的按钮、提示框全部不显示,但地图正常。

尝试过的无效方案

  • z-index: 9999 --- 没用
  • position: fixed --- 没用
  • cover-view 包裹 --- 没用(cover-view 能覆盖 video/map,覆盖不了 web-view)
  • subNVue 子窗口 --- 仍然被 web-view 盖住

根因 :uni-app 的 <web-view> 是原生子窗口(plus.webview.create),绘制层级是系统级别的,高于所有前端渲染层。任何 CSS 手段都无法跨越这个层级鸿沟。

最终方案 :放弃在 Vue 层写 UI,全部 UI 以 HTML 字符串形式通过 evalJS 注入到 web-view 内部去渲染。

js 复制代码
// Vue 侧:构建 UI 并注入
_renderAppUI() {
  var html = '<style>' + this._appStyles() + '</style>'
    + '<div class="location-info">...</div>'
    + '<div class="btn-group">...</div>';
  this._appEvalJS("__mapSetUI('" + html.replace(/'/g, "\\'") + "')");
}
html 复制代码
<!-- map_view.html:接收注入 -->
<div id="uiContainer" onclick="handleUIClick(event)"></div>
<script>
  function __mapSetUI(html) {
    document.getElementById('uiContainer').innerHTML = html;
  }
  // 事件代理,按钮点击回传给 Vue
  function handleUIClick(e) {
    var btn = e.target.closest('[data-id]');
    if (btn) uni.postMessage({type:'buttonClick', id: btn.getAttribute('data-id')});
  }
</script>

另一个小坑是 rpx 单位在 web-view 的普通 HTML 环境中不生效,需要手动转换:

js 复制代码
_rpx(val) {
  const info = uni.getSystemInfoSync();
  return Math.round(val * info.screenWidth / 750);
}

3. 按钮统一定义

App 端按钮在 web-view 内渲染,小程序/H5 端通过 v-for 渲染。为了避免两套按钮定义,共用一份 buttons 数组:

js 复制代码
buttons: [
  { id: 'singleClick',    text: '单次定位' },
  { id: 'conitueClick',   text: '连续定位' },
  { id: 'stopLocation',   text: '停止定位' },
  { id: 'getAddress',     text: '获取地址' },
  { id: 'getGeoCode',     text: '正地理' },
  { id: 'poiSearch',      text: 'poi 搜索' },
  { id: 'poiNearBySearch',text: 'poi 附近搜索' },
  { id: 'getMapCenter',   text: '获取中心点' },
  { id: 'getMapBounds',   text: '获取视野' },
  { id: 'toggleStyle',    text: '暗黑地图' }
]

点击分发也只用一份 methodMap

js 复制代码
handleBtnClick(id) {
  const methodMap = {
    singleClick: this.singleClick,
    conitueClick: this.conitueClick,
    // ...
  };
  if (methodMap[id]) methodMap[id]();
}

App 端从 web-view 的 postMessage 回调走到同一个 handleBtnClick,小程序/H5 端从 @click 直接调用。

4. 逆地址解析

拿到坐标后,调用维智 SDK 的逆地理编码接口获取地址描述:

js 复制代码
import * as wzLocation from '@/utils/wz_sdk_uniapp.js';

getAddress() {
  wzLocation.setAk(LOCATION_KEY);
  wzLocation.getReverseCode(107.191693, 27.945061, 'wgs84').then(
    (res) => {
      this.wzLocation.address.name = res.address.name;
      this.mapController.updateLocation(this.wzLocation);
    }
  );
}

小程序端使用 wgs84 坐标系,与微信 map 组件一致,无需额外转换。

5. POI 搜索功能

关键词搜索和附近搜索同样走维智 SDK:

js 复制代码
// 关键词搜索
poiSearch() {
  wzLocation.setAk(POI_KEY);
  wzLocation.poiSearch("泛悦城T2", '武汉市', 2, 20).then(res => {
    // 跳转到第一个搜索结果
    this.wzLocation.longitude = res[0].geoPoint.split(',')[0];
    this.wzLocation.latitude = res[0].geoPoint.split(',')[1];
    this.mapController.flyTo(this.wzLocation);
  });
}

// 附近搜索
poiNearBySearch() {
  wzLocation.setAk(POI_KEY);
  wzLocation.poiNearbySearch("乒乓球", "体育场馆", '120.59986,31.25979').then(res => {
    // 同上处理
  });
}

搜索结果通过 mapController.addMarker 在地图上打点,位置也可以通过 mapController.flyTo 跳转。

6. H5 端特殊处理

H5 的导航栏默认会遮挡页面,在 pages.json 中配置隐藏:

json 复制代码
{
  "pages": [{
    "path": "pages/index/index",
    "style": {
      "h5": { "navigationStyle": "custom" }
    }
  }]
}

另外移动端浏览器的地址栏会影响 100vh 的计算,需要配合 position: fixed 和安全区适配:

css 复制代码
/* #ifdef H5 */
.content { position: fixed !important; top: 0; left: 0; width: 100%; height: 100%; }
.location-info { top: calc(30rpx + env(safe-area-inset-top)) !important; }
/* #endif */

项目地址

👉 GitHub 仓库地址

后话

我们买的是维智的LBS服务。如果你用的是高德或百度的LBS服务,把上面逆地址解析和POI搜索换成对应平台的API 即可------比如高德的 GET /v3/geocode/regeo 做逆地址,GET /v3/place/text 做 POI 搜索。核心思路不变:map 组件只负责渲染,LBS数据走自己的供应商

改造过程中有些小坑值得注意:

  1. web-view 原生层级是最大的坑,App 端 UI 必须注入到 web-view 内部
  2. rpx 单位在 web-view 的 HTML 环境里无效,需要手动转 px
  3. evalJS 时序不可靠,web-view 内部初始化逻辑要自给自足,evalJS 只做增强不做依赖
  4. 微信 map 不支持运行时切换暗黑主题,需要提前在 pages.json 用条件编译处理

有问题欢迎留言交流。

相关推荐
用户6990304848755 天前
try catch使用场景 处理同步代码错误兼容用的
javascript·uni-app
ITKEY_5 天前
uniapp微信开发者工具 更改AppID失败 touristappid
uni-app
Geek_Vison5 天前
APP瘦身实战:从80MB+砍到15MB——基于小程序容器技术剥离APP非核心业务的实践分享
小程序·uni-app·mpaas
CHB6 天前
HDC2026 演讲实录|AI 驱动的跨端进化:利用 uni-agent 快速构建高性能鸿蒙应用
uni-app·harmonyos
2501_915918416 天前
iOS App性能测试工具的实现方法与优化循环指南
android·ios·小程序·https·uni-app·iphone·webview
斯内普吖6 天前
(开源)高校素拓分管理系统小程序实战指南 基于 Java + SpringBoot + uni-app + Vue + MySQL
java·spring boot·mysql·小程序·uni-app·开源
海阔天空66886 天前
uniapp开启调试模式
uni-app·uniapp开启调试模式
anyup7 天前
分享 5 套 uni-app 实用主题,一键适配暗黑模式
前端·uni-app·视觉设计
gg159357284607 天前
Uni-app跨平台开发全解课程:从零基础到企业级多端落地实战
vue.js·uni-app