
简介面向微信小程序开发者的百度地图接口文件包旨在解决在小程序环境中接入地图展示、地点检索、路径规划、实时定位等地理服务的问题。压缩包内共三十五个文件主体为十个脚本接口文件并配套六个页面结构文件、六个样式文件及六个配置文件另含示例演示和说明文档整体包体仅28KB轻量易部署。文件包遵循微信小程序开发标准开发者可依据文档快速实现覆盖物添加、缩放移动、路线导航及位置共享等常用功能示例页面也为二次开发提供了可直接参考的交互模板。目前已有八十八人学习下载适合希望为小程序快速添加地图能力的前端开发者或学生使用。与一般零散工具包不同这一压缩包以主版本形式提供稳定代码配合官方接口规范即可集成生产环境省去自行封装底层接口的耗时。1. 百度地图微信小程序 jsapi.zip解压出来的是什么怎么用得上做小程序地图功能时最省事的一步就是把百度地图微信小程序 jsapi.zip 拿到手。它里面封装的是百度地图官方为微信小程序场景裁剪过的 JavaScript API 库常见文件是bmap-wx.min.js附带示例页和说明文档。这个东西解决的是“小程序里怎么低成本接入地图能力”的落地问题地图展示、定位、POI 搜索、路线规划都能用几十行代码串起来而不是自己在 WebView 里拼一套 H5 地图。适合正在做社区团购、家政服务这类带位置业务的小程序开发者也适合从零开始接地图、不想被坐标系和底层渲染细节拖住的团队。下面我按接入、显示、检索、避坑和进阶的顺序把整套流程拆开讲清楚。2. 接入微信小程序把 bmap-wx 放进工程三处配置一次跑通下载下来的 zip 是一个压缩包不是直接解压就能跑的小程序完整工程。第一步要做的不是急着打开示例而是理清楚哪些文件进代码库哪些文件只是参考。2.1 解压后该留哪个文件别把整个包塞进项目常见做法是在一个干净目录里解压然后把bmap-wx.min.js复制到小程序的utils或lib目录。包里一般还有 README、示例页面和测试用的图片这些都不需要进主工程。很多人在网上搜“zip解压”之后直接整个目录拖进小程序结果把示例页面、冗余图片一起打进了包后面上传时提示超过 2MB 才回头来删很被动。还有人会搜“zip密码移除”这里多提醒一句官方渠道下载的百度地图小程序 JSAPI 压缩包通常没有密码也不需要什么密码移除操作。如果解压时需要密码说明这个 zip 可能是第三方二次转发的路径不明宁可重新去开放平台下载也不要执行来路不明的解压工具。拿到的包尽量做一次文件列表检查只保留跟地图库相关的文件能省掉后面很多版本混乱的问题。把bmap-wx.min.js放进工程后最主要的工作是初始化。它不是直接 require 进来就能用的需要你在 App 启动时创建一个 BMap 实例。2.2 在 app.js 里初始化并挂到 globalData看下面这段代码这是最基础也最不容易出错的初始化方式// app.js const bmapLib require(./utils/bmap-wx.min.js); App({ globalData: { bmap: null }, onLaunch() { this.globalData.bmap new bmapLib.BMap({ ak: 你在百度地图开放平台申请的密钥 }); } });这里bmapLib是 require 回来的模块对象bmapLib.BMap才是构造函数。构造函数里唯一必填的就是ak也就是百度地图开放平台里“微信小程序”类型应用的密钥。注意它和网页端 JSAPI 的 key 不是同一个东西应用类型选错的话后面所有请求都可能被拒。把实例挂到globalData上的好处是全局只有一份页面之间共用。不要在每个页面的onLoad里重新 new这样既浪费内存也容易让 key 的调用被限流。页面里需要用时写getApp().globalData.bmap或const bmap getApp().globalData.bmap;就够了。初始化只是第一步真正让地图跑起来还要处理三处配置百度侧的 AppID 白名单、微信侧的服务器域名、小程序的定位权限声明。2.3 三个硬性配置appid、合法域名、定位权限声明第一处登录百度地图开放平台找到这个应用的配置页把微信小程序的 AppID 填进白名单。如果不填真机上调用会报类似detailjsapi has been banned的权限错误。第二处在微信公众平台的小程序后台进入“开发管理-服务器域名”把 request 合法域名加上。百度地图微信小程序 JSAPI 内部通过wx.request请求百度服务常见域名是https://api.map.baidu.com。开发阶段可以在开发者工具里勾选“不校验合法域名、web-view、TLS 版本以及 HTTPS 证书”先跑通但上线前必须把这一步补齐否则真机上一片空白。第三处如果地图页要定位和展示当前位置小程序需要在app.json里声明定位权限和隐私接口否则真机上接口会静默失败。{ permission: { scope.userLocation: { desc: 用于在地图上显示你的位置和附近服务 } }, requiredPrivateInfos: [getLocation] }这段配置里desc是用户授权弹窗里展示的文案尽量写得具体一点比如“用于展示你当前的位置和附近门店”。如果后面还要用wx.chooseLocation选点记得把chooseLocation也并进requiredPrivateInfos。这一步漏配置往往是最难查的开发者工具正常真机一调用就fail其实不是代码问题而是权限声明没写。这三个配置都完成之后跑一个最简单的regeocode逆地理编码接口一般就能通。如果还报错先回来看ak是什么时候创建的、应用类型对不对重生成一个 key 再试。3. 地图显示与定位map 组件和 JSAPI 各自负责什么很多人会误以为装了百度地图 JSAPI页面里就能直接出现百度底图。实际上在小程序生态里底图渲染是微信原生map组件的活百度 JSAPI 提供的是数据和计算服务。理解这个分工后面才不会被层级和坐标系问题折腾。3.1 页面里的 map 组件数据来自 setData先看一个最小可用的地图页面模板map idmap classmap longitude{{center.lng}} latitude{{center.lat}} scale{{scale}} markers{{markers}} polyline{{polyline}} show-location{{showLocation}} /map组件的longitude和latitude控制中心点scale是缩放级别范围一般是 3 到 20城市概览用 11 到 13具体街区用 15 到 17。markers是标注点数组polyline是路线折线都能通过setData动态更新。对应的页面数据长这样Page({ data: { center: { lng: 116.404, lat: 39.915 }, scale: 14, markers: [], polyline: [], showLocation: true }, onLoad() { const bmap getApp().globalData.bmap; // 页面后续所有地图服务调用都基于这个实例 } });这组数据里没有直接使用bmap做渲染bmap只负责去请求服务比如反查地址、搜索 POI。页面拿到结果后再通过setData把中心点、标记点、路线点喂给map。如果你发现地图空白但 JSAPI 请求都成功了大概率是数据没有正确写给 map 组件而不是地图服务没返回。这里还要提一个高频问题微信小程序顶部导航栏高度。地图页如果要全屏展示不能直接把高度写成100vh否则会被胶囊按钮、状态栏或底部 tabBar 挡掉一部分。常见做法是用wx.getWindowInfo()拿到整个窗口高度再用wx.getMenuButtonBoundingClientRect()拿到右上角胶囊的位置算出一个安全的地图高度。这样真机适配才不会出现地图底部被遮挡的情况。3.2 用 regeocode 反查当前位置和城市底图能显示以后最常见的需求是“我在哪”。这个接口叫逆地理编码作用是传入经纬度返回地址描述和城市信息。const bmap getApp().globalData.bmap; function fetchAddress(lng, lat) { bmap.regeocode({ location: ${lng},${lat}, success: (res) { console.log(regeocode:, JSON.stringify(res)); this.setData({ address: res.address }); }, fail: (err) { console.error(regeocode fail:, err); } }); }这里最关键的是location参数它是字符串经度,纬度逗号必须是英文逗号。很多人第一次调用时传对象{ lng: 116.404, lat: 39.915 }结果接口直接 fail。另外百度地图不同版本返回结构不完全一样旧教程里可能直接取res.address但新版库里地址可能被包在res.result里。所以第一次跑通后先在success里打印完整结果确认字段再往下写。逆地理编码经常和wx.getLocation配合先拿定位坐标再反查地址然后在地图上方显示“当前城市北京”。注意wx.getLocation需要用户授权首次调用会弹窗用户拒绝后要引导到设置页重新打开否则后续页面一直拿不到坐标。3.3 坐标系转换为什么你看到的点“偏了”这是百度地图接入里最绕、也最容易翻车的地方。微信小程序map组件默认用的是 GCJ-02 坐标系也就是国内常见的“国测局坐标”。而百度地图的定位和地理编码结果官方返回的是 BD-09 百度坐标系。两套坐标系之间存在偏移直接混用点位会偏几十米到几百米具体取决于所在城市。如果你是先通过wx.getLocation拿到坐标这个坐标可以指定为type: gcj02直接给map用没问题。但如果要把这个坐标传给百度 JSAPI 做逆地理编码或 POI 搜索就需要把 GCJ-02 转成 BD-09。反过来百度 JSAPI 返回的路线点和 POI 坐标如果要展示到 map 组件上又得从 BD-09 转回 GCJ-02。下面这两个转换函数是经过检验的常用算法我一般在项目里单独维护成一个utils/coord.js// utils/coord.js function gcj02ToBd09(lng, lat) { const xPi (Math.PI * 3000) / 180; const x lng; const y lat; const z Math.sqrt(x * x y * y) 0.00002 * Math.sin(y * xPi); const theta Math.atan2(y, x) 0.000003 * Math.cos(x * xPi); return { lng: z * Math.cos(theta) 0.0065, lat: z * Math.sin(theta) 0.006 }; } function bd09ToGcj02(lng, lat) { const xPi (Math.PI * 3000) / 180; const x lng - 0.0065; const y lat - 0.006; const z Math.sqrt(x * x y * y) - 0.00002 * Math.sin(y * xPi); const theta Math.atan2(y, x) - 0.000003 * Math.cos(x * xPi); return { lng: z * Math.cos(theta), lat: z * Math.sin(theta) }; } module.exports { gcj02ToBd09, bd09ToGcj02 };建议把坐标转换收口到这一个文件里所有页面都从这里取函数不要在业务代码里各写一份近似公式。否则同一个坐标在列表页和地图页转换结果不一致最后很难排查。我的血泪经验是真机上 marker 偏移一条街第一反应不是怀疑 GPS而是先检查坐标系有没有统一。4. POI 搜索与路线规划把附近数据和路径画到地图上底图、定位、坐标转换都通了地图页基本能立起来。接下来真正有价值的业务功能是“附近有什么”和“怎么去”。这两块分别对应 POI 搜索和路线规划。4.1 search附近 POI 搜索与 marker 渲染做社区团购自提点、家政服务门店这类小程序时搜索周边 POI 是核心功能。百度微信小程序 JSAPI 提供了search方法基本写法如下const bmap getApp().globalData.bmap; bmap.search({ query: 便利店, location: 116.404,39.915, radius: 3000, success: (res) { console.log(search result:, JSON.stringify(res)); const pois res.wxMarkerData || res.result || []; const markers pois.map((item, index) ({ id: index, latitude: item.latitude, longitude: item.longitude, title: item.title || item.name, iconPath: /images/poi.png, width: 28, height: 28 })); this.setData({ markers }); }, fail: (err) { console.error(search fail:, err); } });这里有几个参数值得单独说。query是搜索关键词比如“便利店”“药店”“自提点”location是中心点字符串仍然是经度,纬度的格式radius是搜索半径单位是米一般设 2000 到 5000 比较合适。搜索结果的返回结构在不同版本里不太一样我见过wxMarkerData和result两种容器所以上面做了一次兼容但保险起见还是要先打印确认。markers数组里的iconPath必须是小程序包内的本地路径不能直接用网络图片。如果没做图标map 组件会显示默认红钉能用但不好看最好准备一张 40x40 以内的 PNG压缩一下再放进包。点击 marker 展示详情是小程序地图页几乎必做的交互。给map组件加一个事件绑定map bindmarkertaponMarkerTap .../maponMarkerTap(e) { const id e.detail.markerId; const target this.data.markers.find((m) m.id id); if (target) { wx.showModal({ title: target.title, content: target.address || 暂无地址, showCancel: false }); } }事件回调里拿到的markerId就是我们在markers里设置的id所以构造 marker 时不要偷懒一定要给每个点设置唯一的自增 id。4.2 路线规划drivingRoute / walkingRoute 返回点抽稀“怎么去”在代码层面分成两步先用百度 JSAPI 算出路线折线点再把这些点交给 map 组件的polyline画出来。驾车和步行的调用方式非常相近const bmap getApp().globalData.bmap; bmap.drivingRoute({ origin: { lng: 116.404, lat: 39.915 }, destination: { lng: 116.414, lat: 39.925 }, success: (res) { const routes res.result res.result.routes; if (!routes || !routes.length) { console.warn(no route found); return; } const rawPoints []; routes[0].steps.forEach((step) { (step.points || []).forEach((p) rawPoints.push(p)); }); this.setData({ polyline: [{ points: rawPoints.map((p) bd09ToGcj02(p.lng, p.lat)), color: #3388ff, width: 5, dottedLine: false }] }); }, fail: (err) { console.error(route fail:, err); } });注意这里origin和destination是对象形式而不是字符串和前面search、regeocode的传参习惯不一样。路线规划返回的路线可能不止一条默认取routes[0]即可。每个step里都有一组points把这些点拼起来就是整条路线。路线点往往非常密集一条几公里的路线可能有好几百个点直接全量传给polyline会造成渲染卡顿。我一般会做一次简单抽稀每 3 到 5 个点取一个既能保持路径形状又能减少绘制压力function thinPoints(points, gap 3) { const result []; for (let i 0; i points.length; i gap) { result.push(points[i]); } if (points.length % gap ! 0) { result.push(points[points.length - 1]); } return result; }抽稀后的点仍然需要先经过坐标转换。上面代码里用bd09ToGcj02把百度的 BD-09 坐标转成 GCJ-02再交给polyline才能和底图对齐。如果忘记这一步路线会和道路重叠不上看起来像是在楼顶飞驰。4.3 公交路线与“导航权限”的边界公交路线用transitRoute也能调但入参里通常要额外传城市信息而且不同版本对城市字段的要求不一样有的叫region有的叫city。所以我建议第一次调用时先打印完整成功和失败回调确认当前库到底认哪个字段再写死。公交路线返回的换乘方案更复杂展示到地图上时分段渲染polyline的颜色也可以区分步行和公交路段。关于导航有一个很容易被热搜词带偏的误区很多人搜“发起导航失败请前往百度地图确认权限”以为是微信小程序里可以直接唤起百度地图 App 导航。实际上百度微信小程序 JSAPI 的重点是“地图数据和计算”并不负责拉起百度地图 App。小程序里最稳妥的导航方案是wx.openLocation它会在微信内置地图中展示目标点用户自己选择用哪种方式导航。wx.openLocation({ latitude: gcj02.lat, longitude: gcj02.lng, name: 目的地名称, address: 目的地地址, scale: 16 });这里传入的坐标也必须是 GCJ-02也就是和 map 组件一致的坐标系。如果业务里出现“发起导航失败请前往百度地图确认权限”一类的报错除了检查ak权限还要确认自己是不是用了与小程序不匹配的导航 SDK。我的习惯是小程序端只负责把终点坐标算出来调用wx.openLocation把真正的导航交给微信自带能力稳定也省心。5. 避坑清单接入 bmap-wx 最容易翻车的五个点这套库整体不难但接入过程中我见过太多人卡在同一批问题上。下面五个点按“现象、原因、解决”的顺序列出来基本覆盖了 90% 的初次接入翻车场景。5.1 真机地图空白开发者工具正常现象开发者工具里地图和 marker 都正常扫码到真机上一片空白或者只有灰色网格。原因最常见的是合法域名没配。开发者工具里勾选了“不校验合法域名”后真机调试不受这个开关控制请求仍然会被拦截。其次是map组件的高度为 0或者被父容器用overflow: hidden裁剪掉了。解决先到微信公众平台把百度地图请求域名加进 request 合法域名再把 map 组件的高度设成明确的数值比如600px或calc(100vh - 导航栏高度)不要依赖父容器自动撑开。真机预览时打开调试模式确认 Network 面板里有地图瓦片请求再往下排查。5.2 点位偏移几十米到几百米现象POI 搜索出来的门店在 map 组件上落到了马路对面或者直接漂到隔壁街区。原因坐标系混用。百度接口返回的是 BD-09map 组件用的是 GCJ-02。很多教程没有强调这一点数据从百度接口拿回来直接塞进markers自然偏移。解决在项目里统一维护utils/coord.js所有从百度接口拿回来的坐标在写入 map 组件前先调用bd09ToGcj02。反过来如果要把wx.getLocation的坐标传给百度接口先用gcj02ToBd09转一次。把转换逻辑收口到一个文件不要在页面里各写一套。5.3 报错 detailjsapi has been banned / 10002现象调用search或regeocode时fail 回调返回类似detailjsapi has been banned的文本或者错误码 10002。原因ak不合法或者这个ak没有被授权使用微信小程序 JSAPI。常见误用是把网页端 JSAPI 的 key 直接拿过来或者在百度开放平台忘记填小程序的 AppID 白名单。解决登录百度地图开放平台确认应用类型是“微信小程序”重新生成一个ak替换app.js里的旧值。如果还报 10002检查平台配置里填的 AppID 和当前小程序是不是同一个然后等五到十分钟让配置生效再试。这种权限报错不是代码问题改参数比重启项目有用得多。5.4 source size 2612kb exceed max limit 2mb现象上传代码或真机预览时控制台提示包体积超过 2MB常见文案是source size 2612kb exceed max limit 2mb。原因bmap-wx 库本身并不大很多人的主包里混进了 zip 里附带的示例页面、测试图片或者整个解压目录都被复制进了项目。解决只保留bmap-wx.min.js示例代码和图片一律不进主包。地图用到的 marker 图标压缩到几 KB或者放到 CDN 用网络图片注意网络图片在 marker 上不一定兼容所以更稳妥的做法是本地放一张极小的图标。低频页面可以拆到分包主包只放启动必要的代码。如果项目是用 uniapp 打包成微信小程序也要检查打包配置有没有把不必要的静态资源打进了主包。5.5 回调里拿不到想要的字段现象接口确实走了success但res.wxMarkerData是undefined或者res.address根本不存在。原因库版本和网上教程不一致不同版本返回结构相差很大。网上很多代码是照着老版本写的字段名带wxMarkerData新版可能已经改成result或data。解决第一次调用任何接口先在success回调里用console.log(JSON.stringify(res))把完整结构打印出来。这一步能破解掉回调结构这个“黑匣子”。另外注意回调里的this指向用普通function嵌套会导致setData报错建议统一用箭头函数或者在外层先const that this。替换库文件之前先给当前能跑通的文件做个备份这是给自己留的后悔药。6. 进阶技巧把 JSAPI 调用封装成 Promise 服务层走到这一步接入和排错基本没问题了。接下来值得做的事情是把散落在各个页面的百度地图调用收拢到一个服务层里让业务代码不再关心接口回调结构。6.1 封装一个 bmap-service.js我一般会在utils里建一个bmap-service.js把常用接口包成 Promise// utils/bmap-service.js const getBMap () getApp().globalData.bmap; function regeocode(lng, lat) { return new Promise((resolve, reject) { getBMap().regeocode({ location: ${lng},${lat}, success: resolve, fail: reject }); }); } function searchPoi(query, lng, lat, radius 2000) { return new Promise((resolve, reject) { getBMap().search({ query, location: ${lng},${lat}, radius, success: resolve, fail: reject }); }); } module.exports { regeocode, searchPoi };页面里使用就变成const { regeocode, searchPoi } require(../../utils/bmap-service.js); Page({ async onLoad() { try { const res await regeocode(116.404, 39.915); this.setData({ address: res.address }); } catch (err) { console.error(地址反查失败, err); } } });这样封装有几个直接的好处页面代码不用每次处理回调风格默认参数可以统一维护比如radius只在一个地方改Promise 的catch可以作为全项目的错误上报入口。注意getApp()不能在 App 实例初始化之前调用但页面onLoad阶段已经晚于 ApponLaunch所以没有问题。6.2 真机验证三件套封装完以后最后讲一个我每次交付前都会做的验证流程。第一真机调试时打开 vConsole看 Network 面板里百度的请求是否命中、是否携带了正确的ak。第二如果怀疑请求被拦截用 Charles 抓包确认请求域名和返回码能快速区分是域名配置问题还是密钥问题。第三在展示层写一个小工具函数所有传给map的经纬度都过一次坐标转换并在开发环境打日志一旦出现明显超范围的值就阻止渲染避免黑屏。地图功能看起来玄学其实大部分问题都出在坐标系和权限配置上。我印象最深的一次是接了别人的项目所有 marker 在真机上偏了一条街排查到最后就是百度坐标直接塞给了 map 组件改一个转换函数就好了。希望这份笔记能帮你把这块跑通也少走这段冤枉路。本文还有配套的精品资源点击获取