劃實(shí)戰(zhàn))
簡(jiǎn)介這份資源是面向微信小程序開(kāi)發(fā)者的百度地圖 JavaScript API 接口包適合需要在社交、旅游、打車(chē)等場(chǎng)景中嵌入地理位置服務(wù)的初中級(jí)開(kāi)發(fā)者。包內(nèi)共35個(gè)文件以js腳本、wxss樣式、json配置、wxml模板和png圖片為主壓縮包約28KB其中bmap-wx.js與bmap-wx.min.js為核心接口文件demo目錄提供了可直接參考的頁(yè)面示例README.md則給出基礎(chǔ)說(shuō)明。已有88人學(xué)習(xí)下載。借助這套接口開(kāi)發(fā)者可在小程序內(nèi)實(shí)現(xiàn)地圖展示、標(biāo)記點(diǎn)與多邊形等覆蓋物添加、縮放移動(dòng)、路徑規(guī)劃、地點(diǎn)檢索、駕車(chē)導(dǎo)航、公交查詢(xún)及周邊檢索并支持實(shí)時(shí)位置分享為社交與出行類(lèi)應(yīng)用提供位置能力支撐。文件包命名中的master表明其為主版本穩(wěn)定性較好適合直接用于生產(chǎn)環(huán)境配合官方文檔與參數(shù)示例可幫助開(kāi)發(fā)者快速上手并高效排查調(diào)用問(wèn)題。1. 百度地圖微信小程序 JSAPI 到底解決什么問(wèn)題很多團(tuán)隊(duì)第一次在微信小程序里做地圖第一反應(yīng)是直接用微信原生的map組件。原生組件能顯示地圖、能打點(diǎn)、能畫(huà)線看起來(lái)夠用。但真到業(yè)務(wù)里問(wèn)題就來(lái)了想按關(guān)鍵字搜周邊門(mén)店、想逆地理編碼把經(jīng)緯度換成中文地址、想算兩個(gè)點(diǎn)之間的步行路線、想畫(huà)一片多邊形區(qū)域做電子圍欄——這些原生組件都不直接給。于是就得自己接一套地圖服務(wù)而百度地圖微信小程序 JSAPI 就是干這個(gè)的它把百度地圖的檢索、逆地理、路線規(guī)劃、坐標(biāo)轉(zhuǎn)換等能力封裝成能在小程序里直接調(diào)用的接口。這個(gè)標(biāo)題指向的是一份 JSAPI 的接入包核心價(jià)值在于讓小程序開(kāi)發(fā)者不用從零搭地圖后端就能拿到搜索、定位、算路這些能力。適合兩類(lèi)人一類(lèi)是剛接手小程序地圖需求、想快速跑通的新手另一類(lèi)是已經(jīng)用了原生組件、但被檢索和算路卡住的熟手。下面按「先跑通、再調(diào)參、再避坑」的順序講清楚。2. 接入前的準(zhǔn)備與最小可運(yùn)行工程2.1 申請(qǐng)密鑰與配置合法域名百度地圖的接口調(diào)用都要帶一個(gè) AKAccess Key這是身份憑證。申請(qǐng)流程在百度地圖開(kāi)放平臺(tái)的控制臺(tái)完成創(chuàng)建應(yīng)用時(shí)選「微信小程序」類(lèi)型填上小程序的 AppID平臺(tái)會(huì)生成對(duì)應(yīng)的 AK。這里有個(gè)容易忽略的點(diǎn)AK 是和小程序 AppID 綁定的換一個(gè)小程序就得重新申請(qǐng)不能混用。拿到 AK 之后還要在小程序后臺(tái)配置服務(wù)器域名。百度地圖的接口域名需要加進(jìn) request 合法域名列表否則真機(jī)上請(qǐng)求會(huì)被攔截。常見(jiàn)要加的有api.map.baidu.com和api.map.baidu.com下的路徑。開(kāi)發(fā)階段可以在開(kāi)發(fā)者工具里勾選「不校驗(yàn)合法域名」但上線前必須配好不然線上直接白屏。提示AK 分瀏覽器端、服務(wù)端、小程序端幾種類(lèi)型申請(qǐng)時(shí)選錯(cuò)類(lèi)型會(huì)導(dǎo)致接口返回權(quán)限錯(cuò)誤這個(gè)坑后面避坑章節(jié)會(huì)細(xì)說(shuō)。2.2 引入 JSAPI 文件與初始化這份 JSAPI 包通常是一個(gè)bmap-wx.js之類(lèi)的文件把它放到小程序工程的utils或libs目錄下然后在頁(yè)面里 require 進(jìn)來(lái)。初始化時(shí)傳入 AK創(chuàng)建一個(gè) BMapWX 實(shí)例。// utils/bmap-wx.js 是引入的 JSAPI 文件 const BMapWX require(../../utils/bmap-wx.js); // 初始化ak 換成自己申請(qǐng)的值 const bmap new BMapWX({ ak: 你的小程序端AK }); Page({ data: { markers: [], address: }, onLoad() { // 頁(yè)面加載時(shí)做一次逆地理驗(yàn)證 AK 是否可用 this.reverseGeocode(116.404, 39.915); }, reverseGeocode(lng, lat) { bmap.regeocoding({ location: ${lat},${lng}, // 注意順序是緯度在前 success: (res) { // res 里包含 address、business 等字段 this.setData({ address: res.originalData.result.formatted_address }); }, fail: (err) { console.error(逆地理失敗, err); } }); } });這段代碼做了三件事引入 JSAPI、用 AK 初始化實(shí)例、調(diào)用逆地理接口把經(jīng)緯度轉(zhuǎn)成地址。參數(shù)上location的格式是「緯度,經(jīng)度」順序反了會(huì)返回錯(cuò)誤結(jié)果或空地址這是新手最常翻車(chē)的地方。success回調(diào)里的originalData是百度返回的原始結(jié)構(gòu)formatted_address是格式化后的地址字符串。跑通這一步說(shuō)明 AK 和域名配置都沒(méi)問(wèn)題可以往下做檢索和算路。3. 檢索、逆地理與路線規(guī)劃的落地寫(xiě)法3.1 周邊檢索與關(guān)鍵字搜索的參數(shù)怎么設(shè)周邊檢索search和關(guān)鍵字搜索是地圖業(yè)務(wù)里用得最多的兩個(gè)接口。周邊檢索以某個(gè)中心點(diǎn)為核心按半徑找 POI關(guān)鍵字搜索則是在指定城市范圍內(nèi)按詞找。兩者的參數(shù)結(jié)構(gòu)類(lèi)似但用途不同。// 周邊檢索找中心點(diǎn) 1000 米內(nèi)的咖啡店 bmap.search({ query: 咖啡, location: 39.915,116.404, // 緯度,經(jīng)度 radius: 1000, // 單位米最大一般 50000 page_num: 0, // 頁(yè)碼從 0 開(kāi)始 page_size: 10, // 每頁(yè)條數(shù)最大 20 success: (res) { const pois res.originalData.results.map(item ({ name: item.name, address: item.address, lat: item.location.lat, lng: item.location.lng })); this.setData({ markers: pois }); }, fail: (err) console.error(err) });參數(shù)里radius是半徑單位米設(shè)太大返回結(jié)果會(huì)被截?cái)嘣O(shè)太小又搜不到東西一般按業(yè)務(wù)場(chǎng)景給 500 到 3000。page_num從 0 開(kāi)始翻頁(yè)時(shí)遞增但要注意百度對(duì)總頁(yè)數(shù)有限制深翻頁(yè)拿不到數(shù)據(jù)。page_size最大 20想一次多拿只能靠翻頁(yè)。返回的results數(shù)組里每條 POI 帶location對(duì)象里面是lat和lng直接可以喂給地圖組件的 markers。關(guān)鍵字搜索多一個(gè)region參數(shù)用來(lái)限定城市比如region: 北京。不傳 region 時(shí)結(jié)果可能跨城做本地生活類(lèi)業(yè)務(wù)一定要傳。3.2 逆地理編碼與坐標(biāo)轉(zhuǎn)換的配合逆地理編碼是把經(jīng)緯度換成地址前面已經(jīng)跑通。但這里有個(gè)坐標(biāo)系問(wèn)題必須講清楚百度地圖用的是 BD09 坐標(biāo)系而微信小程序wx.getLocation默認(rèn)返回的是 GCJ02 坐標(biāo)系。兩者不轉(zhuǎn)換直接用位置會(huì)偏移幾百米這就是很多人說(shuō)的「玄學(xué)偏移」。// 把微信拿到的 GCJ02 坐標(biāo)轉(zhuǎn)成 BD09 bmap.translate({ from: gcj02, to: bd09, location: 39.915,116.404, success: (res) { // res.originalData.result 里是轉(zhuǎn)換后的坐標(biāo)數(shù)組 const point res.originalData.result[0]; console.log(轉(zhuǎn)換后, point.y, point.x); // y 是緯度x 是經(jīng)度 }, fail: (err) console.error(err) });translate接口的from和to支持wgs84、gcj02、bd09三種坐標(biāo)系互轉(zhuǎn)。注意返回結(jié)構(gòu)里y對(duì)應(yīng)緯度、x對(duì)應(yīng)經(jīng)度和常見(jiàn)的lat/lng命名不一樣取值時(shí)別搞反。實(shí)際業(yè)務(wù)里通常是先用wx.getLocation拿到 GCJ02 坐標(biāo)轉(zhuǎn)成 BD09 后再去調(diào)檢索或逆地理這樣結(jié)果才準(zhǔn)。3.3 路線規(guī)劃接口的調(diào)用與結(jié)果解析路線規(guī)劃分駕車(chē)、步行、騎行、公交幾種接口名不同但參數(shù)結(jié)構(gòu)接近。以駕車(chē)為例傳入起點(diǎn)和終點(diǎn)坐標(biāo)返回的是分段路徑和總距離、總時(shí)長(zhǎng)。bmap.driving({ origin: 39.915,116.404, // 起點(diǎn) 緯度,經(jīng)度 destination: 39.925,116.424, // 終點(diǎn) success: (res) { const route res.originalData.result.routes[0]; console.log(總距離(米), route.distance); console.log(總耗時(shí)(秒), route.duration); // route.steps 是分段導(dǎo)航信息 route.steps.forEach(step { console.log(step.instructions); // 每段文字指引 }); }, fail: (err) console.error(err) });返回的routes是數(shù)組一般取第一條。distance單位米duration單位秒展示時(shí)要自己換算成公里和分鐘。steps里每段帶instructions文字指引和path坐標(biāo)串畫(huà)線時(shí)把 path 解析出來(lái)喂給 polyline 即可。公交規(guī)劃返回結(jié)構(gòu)更復(fù)雜帶換乘信息解析時(shí)要多判空。4. 避坑與常見(jiàn)問(wèn)題排查4.1 AK 類(lèi)型選錯(cuò)導(dǎo)致接口全部報(bào)錯(cuò)現(xiàn)象所有接口都返回權(quán)限錯(cuò)誤或狀態(tài)碼非 0控制臺(tái)提示 AK 無(wú)效。原因是申請(qǐng)時(shí)選了瀏覽器端或服務(wù)端類(lèi)型而小程序端必須用「微信小程序」類(lèi)型的 AK。解決方式是回控制臺(tái)重新創(chuàng)建應(yīng)用類(lèi)型選對(duì)把新 AK 換進(jìn)初始化代碼。這個(gè)坑很隱蔽因?yàn)?AK 字符串本身看起來(lái)沒(méi)問(wèn)題只有類(lèi)型不對(duì)。4.2 坐標(biāo)系沒(méi)轉(zhuǎn)換導(dǎo)致位置偏移幾百米現(xiàn)象地圖上打的點(diǎn)和實(shí)際位置對(duì)不上偏移量穩(wěn)定在幾百米。原因是微信定位返回 GCJ02百度接口吃 BD09中間少了轉(zhuǎn)換。解決辦法是在拿到定位坐標(biāo)后先調(diào)translate轉(zhuǎn)成 BD09再用于檢索、逆地理和打點(diǎn)。判斷是否偏移可以拿一個(gè)已知地標(biāo)的坐標(biāo)對(duì)比偏幾百米基本就是坐標(biāo)系問(wèn)題。4.3 真機(jī)請(qǐng)求被攔截而開(kāi)發(fā)者工具正?,F(xiàn)象開(kāi)發(fā)者工具里一切正常真機(jī)上接口全部失敗。原因是沒(méi)在小程序后臺(tái)配置 request 合法域名工具里勾了「不校驗(yàn)合法域名」所以看不出來(lái)。解決方式是登錄小程序后臺(tái)在開(kāi)發(fā)設(shè)置里把百度地圖的接口域名加進(jìn)合法域名列表然后重新編譯真機(jī)預(yù)覽。4.4 翻頁(yè)參數(shù)用錯(cuò)導(dǎo)致重復(fù)數(shù)據(jù)現(xiàn)象翻頁(yè)拿到的 POI 和上一頁(yè)重復(fù)或者直接返回空。原因是page_num從 0 開(kāi)始有人從 1 開(kāi)始傳導(dǎo)致第一頁(yè)數(shù)據(jù)被跳過(guò)或重復(fù)。另外百度對(duì)深翻頁(yè)有限制翻到后面可能拿不到數(shù)據(jù)。解決辦法是頁(yè)碼嚴(yán)格從 0 遞增并在業(yè)務(wù)上做去重不要依賴(lài)接口保證唯一。4.5 回調(diào)里 this 指向丟失現(xiàn)象在 success 回調(diào)里用this.setData報(bào)錯(cuò)提示 this 是 undefined。原因是普通函數(shù)的 this 不指向頁(yè)面實(shí)例。解決辦法是把回調(diào)寫(xiě)成箭頭函數(shù)或者在調(diào)用前用const that this緩存。這個(gè)是小程序開(kāi)發(fā)的通用坑不只是地圖接口會(huì)遇到。5. 進(jìn)階把地圖能力封裝成可復(fù)用模塊跑通單個(gè)接口之后真正影響維護(hù)成本的是怎么組織代碼。我一般會(huì)把地圖相關(guān)調(diào)用統(tǒng)一收進(jìn)一個(gè) service 層頁(yè)面只負(fù)責(zé)拿數(shù)據(jù)渲染不直接碰 JSAPI。這樣換 AK、改參數(shù)、加緩存都只動(dòng)一個(gè)地方。// services/mapService.js const BMapWX require(../utils/bmap-wx.js); const bmap new BMapWX({ ak: 你的AK }); // 統(tǒng)一封裝成 Promise頁(yè)面里用 async/await function searchNearby(keyword, lat, lng, radius 1000) { return new Promise((resolve, reject) { bmap.search({ query: keyword, location: ${lat},${lng}, radius, page_num: 0, page_size: 20, success: (res) resolve(res.originalData.results || []), fail: reject }); }); } // 坐標(biāo)轉(zhuǎn)換也封裝一層避免頁(yè)面里到處寫(xiě) function gcj02ToBd09(lat, lng) { return new Promise((resolve, reject) { bmap.translate({ from: gcj02, to: bd09, location: ${lat},${lng}, success: (res) { const p res.originalData.result[0]; resolve({ lat: p.y, lng: p.x }); }, fail: reject }); }); } module.exports { searchNearby, gcj02ToBd09 };封裝成 Promise 之后頁(yè)面里的調(diào)用就干凈很多const mapService require(../../services/mapService.js); Page({ async onLoad() { const loc await this.getLocation(); const bd await mapService.gcj02ToBd09(loc.latitude, loc.longitude); const pois await mapService.searchNearby(超市, bd.lat, bd.lng, 2000); this.setData({ markers: pois }); }, getLocation() { return new Promise((resolve, reject) { wx.getLocation({ type: gcj02, success: resolve, fail: reject }); }); } });這樣做的收益在項(xiàng)目變大后特別明顯接口參數(shù)要調(diào)、AK 要換、要加失敗重試都只改 service 層。另外建議給檢索結(jié)果加一層本地緩存同一個(gè)中心點(diǎn)短時(shí)間內(nèi)重復(fù)搜同一關(guān)鍵字直接讀緩存能省調(diào)用量也能提速。緩存 key 用「關(guān)鍵字坐標(biāo)半徑」拼設(shè)個(gè)幾分鐘過(guò)期就行。驗(yàn)證封裝是否可靠我習(xí)慣寫(xiě)一個(gè)最小頁(yè)面只做「定位→轉(zhuǎn)換→檢索→打點(diǎn)」這一條鏈路跑通再往業(yè)務(wù)里搬。真機(jī)上重點(diǎn)看三件事定位準(zhǔn)不準(zhǔn)、點(diǎn)打的位置對(duì)不對(duì)、翻頁(yè)數(shù)據(jù)重不重。這三樣沒(méi)問(wèn)題基本就能上線了。我自己踩得最狠的一次是忘了轉(zhuǎn)坐標(biāo)系測(cè)試機(jī)上看著差不多真機(jī)到現(xiàn)場(chǎng)偏了一條街返工查了半天才定位到是 GCJ02 和 BD09 混用。后來(lái)養(yǎng)成習(xí)慣凡是坐標(biāo)進(jìn)地圖接口前先問(wèn)一句「這是哪個(gè)坐標(biāo)系」。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取