从外卖到打车:手把手教你为小程序集成‘附近’功能(基于uni-app和wx.getFuzzyLocation)
当用户打开外卖小程序时,最关心的往往是"附近有什么好吃的";使用打车软件时,系统自动定位的准确度直接影响体验。这些场景背后,都依赖LBS(基于位置服务)技术的精准实现。本文将带你用uni-app框架,从商业逻辑出发,完整构建一个"附近商家"功能模块。
1. 商业场景分析与技术选型
在开发"附近"功能前,需要明确业务场景对精度的要求。外卖类应用通常需要50-100米精度即可满足"附近商家"展示需求,而打车软件则需要更高精度的定位来确定上车点。微信小程序提供了两种定位接口:
wx.getFuzzyLocation:模糊定位(精度约100-500米)wx.getLocation:精确GPS定位(需单独申请权限)
对于大多数O2O场景,模糊定位完全够用且更容易通过审核。以下是不同业务场景的定位需求对照表:
| 业务类型 | 推荐接口 | 适用场景 | 审核通过率 |
|---|---|---|---|
| 外卖/团购 | getFuzzyLocation | 商家列表展示 | 85%+ |
| 打车/共享单车 | getLocation | 精准上车点确认 | 60%左右 |
| 社交/活动 | getFuzzyLocation | 附近用户/活动推荐 | 90%+ |
提示:首次申请定位权限时,建议先使用模糊定位接口,待小程序有一定用户基数后再申请精确定位,通过率会显著提高。
2. 权限申请与项目配置实战
2.1 微信后台权限申请技巧
在微信公众平台申请getFuzzyLocation权限时,"申请理由"的撰写直接影响审核结果。避免使用"需要获取用户位置"这类泛泛描述,而应该:
- 明确具体使用场景(如"展示用户周边3公里内的餐饮商家")
- 说明对用户的价值(如"减少手动输入地址的麻烦")
- 承诺数据用途(如"仅用于当前页面展示,不会存储位置数据")
一个通过率较高的申请案例:
用于向用户展示其所在位置周边3公里内的合作商家信息(包括餐饮、超市等),提升本地生活服务匹配效率。位置数据仅用于实时计算距离,不会存储到服务器。2.2 uni-app项目配置详解
在HBuilderX中创建uni-app项目后,需要配置两个关键文件:
manifest.json的微信小程序专属配置:
"mp-weixin": { "permission": { "scope.userFuzzyLocation": { "desc": "用于展示您附近的优惠活动和商家" } }, "requiredPrivateInfos": ["getFuzzyLocation"] }- 具体页面的
page.json配置:
{ "permission": { "scope.userFuzzyLocation": { "desc": "您的位置将用于计算与商家的距离" } } }注意:
desc字段会直接显示在用户授权弹窗上,建议用用户能理解的业务语言,而非技术术语。
3. 核心代码实现与优化
3.1 基础定位功能实现
在页面中调用定位接口时,建议添加完整的错误处理:
async getLocation() { try { const res = await uni.getFuzzyLocation({ type: 'wgs84' // 标准GPS坐标 }); this.longitude = res.longitude; this.latitude = res.latitude; // 触发商家列表更新 this.loadNearbyShops(); } catch (err) { console.error('定位失败:', err); uni.showToast({ title: '无法获取位置,请检查权限设置', icon: 'none' }); // 降级方案:使用默认城市中心坐标 this.useFallbackLocation(); } }3.2 性能优化实践
频繁调用定位接口会导致用户体验下降,推荐以下优化策略:
缓存机制:将获取的位置信息存储在本地,30分钟内不再重复请求
智能触发:结合页面生命周期和用户操作:
onShow() { // 每次进入页面检查是否有缓存位置 if (!this.locationCache || this.isCacheExpired()) { this.getLocation(); } } onPullDownRefresh() { // 下拉刷新时强制更新位置 this.getLocation(); }节流控制:限制定位按钮的点击频率:
let lastClickTime = 0; handleLocationClick() { const now = Date.now(); if (now - lastClickTime < 3000) { return; } lastClickTime = now; this.getLocation(); }
4. 业务联调与数据展示
4.1 与后端API的交互设计
获取到用户坐标后,通常需要传递给后端获取附近商家。推荐使用以下参数结构:
{ longitude: 116.404, // 经度 latitude: 39.915, // 纬度 range: 3000, // 搜索范围(米) sort: 'distance', // 按距离排序 category: 'food', // 商家分类(可选) limit: 20 // 返回结果数 }4.2 前端展示最佳实践
商家列表页的UI设计直接影响转化率,关键要素包括:
距离可视化:
- 1km内显示"约XX米"
- 1-3km显示"约XX公里"
- 超过3km建议不展示或标注"距离较远"
智能排序逻辑:
sortShops(shops, userLoc) { return shops.sort((a, b) => { // 优先展示营业中的店铺 if (a.isOpen !== b.isOpen) { return a.isOpen ? -1 : 1; } // 其次按距离排序 const distA = this.calcDistance(userLoc, a); const distB = this.calcDistance(userLoc, b); return distA - distB; }); }地图集成方案:
<map id="nearbyMap" :longitude="longitude" :latitude="latitude" scale="15" :markers="markers" @markertap="handleMarkerTap" style="width: 100%; height: 300rpx;"> </map>
5. 异常处理与用户体验
5.1 定位失败时的降级方案
当无法获取用户位置时,可依次尝试以下策略:
- 引导用户手动选择所在区域
- 使用IP定位获取城市级位置
- 显示平台推荐的热门区域
对应的实现代码:
async useFallbackLocation() { // 方案1:读取用户上次选择的地址 const savedAddress = uni.getStorageSync('lastUsedAddress'); if (savedAddress) { this.location = savedAddress; return; } // 方案2:调用IP定位接口 try { const ipLocation = await this.getIPLocation(); this.location = ipLocation.city; } catch { // 方案3:使用默认城市 this.location = this.defaultCity; } }5.2 权限引导策略
用户拒绝授权后,应该:
- 解释为什么需要位置权限(用业务价值而非技术需求)
- 提供图文引导说明如何重新开启权限
- 允许用户手动输入地址
示例引导弹窗内容:
【附近优惠】需要您的位置权限 才能为您推荐: ✓ 500米内的限时折扣 ✓ 步行可达的优质商家 ✓ 专属位置优惠券 [去设置] [手动选择地址]在实际项目中,我们团队发现将定位功能与具体的业务优惠挂钩(如"开启定位获取专属新人红包"),可以将授权率从40%提升到75%以上。