ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微信小程序垃圾分类识别开发实战:从uni-app选型到上线踩坑全记录

微信小程序垃圾分类识别开发实战:从uni-app选型到上线踩坑全记录 1. 从“随手一拍”到“结果靠谱”这个项目到底在解决什么问题垃圾智能分类小程序光看名字会让人产生一种错觉——这不就是调个图像识别接口拿相机拍一下垃圾告诉用户“这是可回收物”就完事了等真把项目做下来才发现真正的难点根本不在识别接口而在“用户随手一拍之后系统给出的答案能不能让他信任”这一整条链路。我接手这个项目时产品经理的需求文档写得也很简单“做一个拍照识别垃圾分类的微信小程序”但我知道如果只是把识别结果甩到用户脸上那这个产品和宣传页上的小工具没有区别用户用完一次就不会再打开了。1.1 一个被反复吐槽的真实场景先还原一下真实使用场景。用户站在垃圾桶前手里拎着一袋东西打开小程序拍照识别结果弹出来——“干垃圾”。对这个场景看起来挺顺的但接下来用户一定会问三个问题为什么它是干垃圾我手里的奶茶杯算干垃圾还是可回收物如果我分错了会怎么样这三个问题直接决定了小程序的功能边界。如果只做“识别”那用户不信任你如果做了“解释”用户会觉得你专业如果做了“纠错反馈”用户才会真正留下来甚至愿意帮你补充垃圾分类知识库。所以我把这个项目的核心拆成了三部分拍照识别、分类知识解释、用户反馈闭环。识别只是一个入口后续的知识展示和纠错机制才是真正建立用户信任的地方。1.2 项目的价值边界与功能范围在和团队确认过一轮之后最终版本的功能范围是这样定的拍照/相册识别接入图像识别接口识别出垃圾类型及具体物品名称。搜索与知识库支持文字搜索“一次性纸杯”“电池”“过期药品”等能查分类、查投放要求。识别结果解释展示为什么属于这个分类给出投放前的注意事项比如“倒掉液体再扔”。用户纠错反馈用户对结果有异议时可以提交纠错后台定期更新数据库。收藏与历史让用户能查自己查过的记录对高频物品做收藏。这里面每一个功能单独拎出来都不难但合在一起就成了一个完整的“微型知识产品”。我在做项目规划时反复提醒自己小程序的技术难度是有限的真正的门槛在内容组织、交互节奏和数据准确性上。这也决定了后面选型、开发顺序、联调阶段的很多决策。2. 选型阶段的三个选择题uni-app、识别引擎和后端方案这个项目一开始就有个隐性需求不一定只发微信小程序后面可能还要出App端和H5版。所以在技术选型上我花了整整两天把“原生微信小程序”和“uni-app”对比了一遍。2.1 原生微信小程序还是uni-app如果你只做微信小程序而且以后绝对不碰App那我直接建议原生别犹豫。原生小程序的组件和API更新最快社区踩坑资料也最多调试体验最顺。但考虑到我们团队后面确实要做App端甚至要用同一个账号体系打通多端我选了uni-app配合HBuilderX来开发。这么选有几个实际考量一套代码编译到微信小程序、App、H5避免重复开发。uni-app的语法整体上还是Vue风格团队成员上手成本低。插件市场里有大量现成组件比如后面会用到的日期选择器、拖拽排序组件省去自己造轮子的时间。HBuilderX本身集成了微信开发者工具的调用编译后直接打开调试流程很顺。当然选uni-app也意味着要接受它的“中间层”问题。最典型的就是某些小程序原生能力uni-app没有封装或者封装得不彻底。比如长按拖拽滚动、右上角胶囊按钮控制、web-view通信这类偏底层的能力你仍然要写条件编译代码或者直接调微信小程序的API。我的经验是遇到这种情况不要硬绕直接用#ifdef MP-WEIXIN写原生兼容代码比在uni-app里找替代方案靠谱得多。2.2 垃圾分类识别云端API、本地模型还是民间字典识别引擎是整个项目里最容易被“过度设计”的部分。当时团队里有人提议自己训练一个YOLOv5模型部署到服务器上做实时识别。我听完就否决了。垃圾分类的物品类别非常多而且很多物品从照片上看几乎一样——一杯没喝完的奶茶和一杯白开水从“识别”角度是完全不同的垃圾类型。这种细粒度分类问题自己训练模型需要海量标注数据做出来准确率也很难看。最务实的方案是三层结合第一层调用第三方图像识别API。市面上有专门的垃圾分类识别接口准确率对常见物品基本够用返回结果包含物品名称和分类。第二层维护本地关键词表。对API返回的结果做二次映射。比如API识别出“塑料瓶”本地表里可以补充“矿泉水瓶”“饮料瓶”等别名并附带详细的投放说明。第三层文本搜索兜底。如果用户拍出来的照片本身不清晰识别结果大概率不准这时引导用户切换到文字搜索直接用关键词查知识库。这三个层次互相补充既不盲目依赖外部接口也不自己硬扛模型训练。很多人把精力浪费在“让识别结果更准”上实际上产品层面完全可以通过搜索兜底和纠错反馈来消化识别不准的问题。2.3 后端云开发、PHP还是无后端后端方案我也纠结了很久。最初想用微信云开发因为它的免鉴权、云数据库、云存储能极大缩短开发周期。但产品明确要求“后端数据要能导出、要做管理后台、要支持后续App端共用”云开发在这类场景下虽然也能用但意味着要把管理后台也搬到云函数里复杂度不小。最后后端用了传统方案PHP提供JSON接口小程序端用uni.request对接。选择PHP不是因为它技术先进而是因为团队里最熟悉的是PHP而且服务器的运维成本最低。后端接口这块我的经验是不要为了“先进”而选型。项目管理方最关心的是稳定和可维护PHP配合Nginx跑一套RESTful接口完全够用。登录认证这里也必须提一下。微信小程序登录的标准流程是前端调uni.login()拿到临时code把code传给后端后端再拿着code去微信接口换openid和session_key然后自己下发一套token。这个“code换token”的过程在小程序里是登录的核心闭环。很多新人会误以为uni.login()返回的就是用户身份其实它只是获取用户身份的“入场券”真正的用户身份必须靠后端去微信服务器换取。这个逻辑不搞清楚后面做用户体系会各种出问题。3. 产品功能落地识别流程、知识库与页面节奏功能范围定好了接下来就是怎么落地。我画页面原型的时候没有按照“首页、识别页、我的”这种常规三段式去切而是按用户的使用动线来设计页面。3.1 识别流程拍照、结果、纠错闭环识别流程我分成了四步第一步用户进入首页最醒目的是一个大按钮“拍一下”第二步拍照或选择相册图片后上传到后端第三步后端调用识别接口返回物品名称和分类第四步结果页展示分类、物品名称、投放提示如果用户不认可结果可以点击“我不同意”提交纠错。这四步里面最容易出错的是第二步的图片上传。照片方向信息EXIF会导致图片被旋转尤其苹果手机拍出来的竖屏照片经常在上传到服务端后变成横的。解决这个问题我在前端做了两件事用uni.compressImage压缩图片大小减少上传流量同时在后端用PHP读取图片的EXIF方向信息对图片做自动旋转处理。只依赖任何一端都不够保险必须前后端同时处理。结果页还加了一个小小但非常重要的设计展示“投放提示”。比如识别结果是“一次性纸杯”投放提示就是“请倒掉剩余液体压扁后投放”。这个提示不是我自己编的而是对照各城市的垃圾分类指引逐条整理的。不要小看这一行字它直接决定了用户对结果的信任度。3.2 数据库结构分类树与别名表知识库的数据库设计我是按照“分类树别名表”来做的。核心三张表第一张category表存一级分类也就是可回收物、厨余垃圾、有害垃圾、其他垃圾四类。每一条记录带分类名称、分类颜色、投放说明。第二张item表存具体物品。字段包括物品名称、所属分类ID、投放提示、图片URL、审核状态。物品的名称必须统一为规范名比如“塑料袋”不能同时出现“塑料袋”“朔料袋”“塑料袋子”这种混乱写法。第三张item_alias表存别名映射。一个物品可以有多个别名比如“矿泉水瓶映射到塑料瓶”“充电电池映射到有害垃圾”。文本搜索的时候先查别名表再把别名关联到具体物品上。这套数据库结构看起来简单但实际使用中非常稳定。尤其是别名表它让搜索命中率大幅提升也方便运营人员持续扩充词库。用户端反馈的“纠错”数据最终也会由运营人员审核后决定是否新增别名或修改分类映射。3.3 页面设计中容易被忽略的交互细节页面交互上有三个细节是开发过程中反复调整过的。第一个是搜索页的单选框。分类筛选功能我用了自定义单选框而不是原生radio-group。原因很简单原生radio的样式在小程序里比较死板而且选中状态和我们的视觉规范差距很大。自定义单选框要注意的点是选中和未选中的状态差异必须足够明显不能只换颜色深浅最好一个实心、一个空心色弱用户也能分得清。第二个是长按拖拽滚动排序。我在“我的收藏”列表里做了长按拖拽调整顺序的功能。这里有个大坑当列表放在scroll-view里时长按触发拖拽和滚动方向会冲突。解决办法是用catchtouchmove阻断拖拽时的滚动同时通过touches[0].clientY实时计算移动距离更新transform位移。不要用scroll-view的滚动偏移去计算拖拽位置那样会越算越乱。第三个是加载页。微信小程序修改刚进入时的加载页面通常是指在app.json里配置window的背景色和导航栏样式再加一个自定义的骨架屏页面。如果只是换启动背景图小程序本身是不支持的。我们最终的做法是把首页设计成骨架屏结构数据加载完成后再渲染真实内容这样用户进入时感觉不到白屏等待体验会好很多。4. 开发实录我在这个项目里卡得最久的五个问题这个项目的开发周期总共六周前两周非常顺利后面四周几乎都在和各类“奇奇怪怪”的问题作斗争。我把印象最深的五个问题记录下来都是Google半天、翻论坛翻到眼瞎才解决的。4.1 顶部导航栏高度“一厘米”的适配折腾第一个问题来自顶部导航栏。我们把首页设置成了自定义导航栏因为想在右上角放一个扫描按钮而不是干巴巴的标题文字。自定义导航栏的第一步就是计算状态栏高度和导航栏高度。状态栏可以通过uni.getSystemInfoSync().statusBarHeight拿到但这个高度只是最上面的信号栏高度。真正麻烦的是右下角胶囊按钮的高度和位置只有在这个基础上才能算出导航栏的真实高度。正确的计算方式是const systemInfo uni.getSystemInfoSync() const menuButton uni.getMenuButtonBoundingClientRect() // 导航栏高度 (胶囊顶部到屏幕顶部的距离 - 状态栏高度) * 2 胶囊高度 const navBarHeight (menuButton.top - systemInfo.statusBarHeight) * 2 menuButton.height const navBarTop systemInfo.statusBarHeight const navBarBottom menuButton.bottom这里要说明一下用“胶囊顶部位置减去状态栏高度再乘以2加上胶囊高度”算出来的导航栏高度才是视觉上和胶囊中心对齐的结果。如果只是粗暴地把状态栏高度加一个固定值在刘海屏、灵动岛这些设备上一定会偏移。另外不要缓存这个高度因为每一台设备的缩放参数不同进入页面时实时计算最稳妥。4.2 uni-datetime-picker 放在 scroll-view 里的iOS渲染问题这个项目里有一个功能是查询“历史投放记录”需要选择日期范围。我当时直接把uni-datetime-picker放在了页面的scroll-view里Android端测试一切正常结果一到iOS上就出问题——日历弹层的位置完全错乱有时候弹出来是空的有时候点到其他地方弹层就消失。后来查了很久才发现这不是uni-datetime-picker组件本身的问题而是iOS在小程序的scroll-view渲染机制里对position: fixed的弹层支持有问题。弹层虽然设置了fixed定位但因为父节点在scroll-view内部iOS的WebView渲染引擎在滚动状态下对fixed元素的处理不符合预期弹层就会跟着滚动块一起移动或者直接不渲染。解决办法是不要把这个组件直接放在scroll-view里边。我最终把日期选择器单独抽出到页面scroll-view外层用一个固定定位的容器承载确保弹层的父级不是滚动容器。如果你的页面结构确实无法抽出组件也可以用popup弹窗组件包一层把日期选择器放进弹窗里等弹窗出现时再渲染日期选择器也能绕开这个问题。这个坑的本质是iOS渲染机制和Android不完全一致写小程序时凡是遇到fixed定位弹层异常第一时间检查父级是不是滚动容器。4.3 this.setData 嵌套字段写法的坑第三个问题非常基础但确实浪费了我半天时间。当时要在登录回调里更新用户信息一开始写的是这样的代码this.setData({ userinfo.nickname: that.data.nickname })结果直接报错。原因很简单——data里userinfo是一个对象你要更新对象内部的nickname字段必须把字段路径用引号包起来that.setData({ userinfo.nickname: res.data.nickname })这里还有一个细节容易忽略如果userinfo对象在初始化时根本没有定义nickname这个字段直接按路径更新是没问题的但如果你更新了一个不存在的父级路径比如userinfo都没定义那setData会因为找不到父级而报错。所以初始化data的时候一定要把对象结构先声明完整哪怕值是空字符串data: { userinfo: { nickname: , avatar: } }另外setData操作是异步的但它也带一个回调函数。如果你需要在视图更新后执行逻辑比如根据最新的数据判断是否展示某个区域一定要用回调不要在setData后面直接读this.data以为已经是最新值。我们在这个项目里就出现过下拉刷新后列表数据已经更新了但骨架屏还是没消失的问题就是因为没有在回调里关闭加载状态。4.4 wx.env.USER_DATA_PATH 保存附件的正确打开方式“保存附件”这个需求当时产品说要支持把垃圾分类清单导出成Excel再保存到本地。这里用到了wx.env.USER_DATA_PATH。注意这个环境变量名官方文档里是全大写的USER_DATA_PATH不是小写。很多人写代码的时候图省事敲成小写结果死活拿不到路径。USER_DATA_PATH是小程序本地用户目录每个小程序在自己的空间内可以读写文件但这个目录在iOS和Android上的物理位置不一样你不能在前端拼一个绝对路径去访问。正确做法是用FileSystemManager操作const fs wx.getFileSystemManager() fs.writeFile({ filePath: ${wx.env.USER_DATA_PATH}/recycle_guide.csv, data: csvContent, encoding: utf8, success: (res) { // 写完之后用户可以选择分享或打开文档 wx.openDocument({ filePath: ${wx.env.USER_DATA_PATH}/recycle_guide.csv, fileType: csv, showMenu: true }) }, fail: (err) { console.error(保存失败, err) } })这里要特别注意一个知识点USER_DATA_PATH下保存的文件只有你这个小程序自己才能访问。用户如果想把这个文件转发到微信聊天里需要在上面的wx.openDocument里设置showMenu: true这样右上角菜单里才会出现“转发给好友”和“保存到手机”的选项。如果不设置showMenu用户根本没法把文件导出去产品经理就会来找你“为什么保存了却找不到文件”。4.5 图片旋转和单选组件排版图片旋转的问题前面已经说了通过前后端同时处理就能解决。这里只补充一个经验原图不要直接上传。uni.compressImage不仅能压缩体积还能在一定程度上纠正部分手机拍照的方向因为压缩接口内部会重新编码图片丢掉一些异常的旋转信息。当然这不能完全替代服务端的EXIF处理两者配合使用才最稳。单选组件的排版我们在“分类筛选”页面踩了一个跟scroll-view类似的坑自定义单选按钮做成了flex横向排列当选项超过屏幕宽度时自然换行之后的间距非常难看。解决方式是给每个选项设置flex: 0 0 auto并且用flex-wrap: wrap配合margin-right控制间距最后用一个margin-left: auto的技巧让换行后的行首不错位。这个东西听起来不起眼但视觉细节就是网站的整体品质感。5. 联调、抓包和跨端通信后端对接的一手经验这个项目后端用PHP写接口前后端联调阶段遇到的问题比开发阶段还多。有些问题纯粹是接口字段定义不清晰造成的但有些问题必须靠抓包才能定位。5.1 Charles抓包把电脑变成小程序的调试窗口微信小程序在手机端的网络请求默认不是那么容易直接看到的。做前后端联调时如果后端说“我没收到你的请求”而前端说“我明明发请求了”这种扯皮的事情最好的解决办法就是用Charles抓包。Charles抓包小程序的原理并不复杂手机和电脑连同一个局域网手机设置HTTP代理指向电脑的IP和端口Charles安装根证书就能解密HTTPS流量。这样一来小程序发出的每个请求、请求头、请求体、响应体都能在电脑上看得清清楚楚。实际操作中我发现最影响效率的是“过滤条件”。手机上的小程序App请求非常多不仅有业务接口还有微信自身的统计、日志请求如果不设置过滤Charles控制台会被刷爆。我一般是直接在Charles的Proxy - SSL Proxying Settings里只保留目标域名的SSL代理然后在Focus区域只关注后端接口所在域名其他流量一律不关注。这里要提醒一句小程序的生产环境强制要求HTTPS和域名备案但开发阶段可以在微信开发者工具的“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。不要因为没配域名就怀疑自己代码写错了先把这个开关打开什么问题都能快速暴露出来。5.2 后端用PHP对接uni.request的流程PHP后端接收小程序请求接口格式一般是这样的// 标准JSON响应格式 public function classify(Request $request) { $imageUrl $request-input(image_url); // 调用识别API得到分类结果 $result recognize($imageUrl); return response()-json([ code 0, msg success, data [ item_name $result[name], category $result[category], tip $result[tip] ] ]); }前端uni.request的写法是uni.request({ url: https://api.example.com/classify, method: POST, data: { image_url: tempFilePath }, header: { Content-Type: application/json, Authorization: Bearer token }, success: (res) { if (res.data.code 0) { // 处理分类结果 } } })后端接口有一个统一规范必须提前定好不管成功失败响应体里一定要有code字段0表示成功非0表示失败并且msg字段要写清楚失败原因。这样小程序端才能统一拦截处理而不是每次都要去猜后端这个接口返回的是status还是success还是code。我们在这个项目里因为前后端字段命名不一致联调阶段改了三轮血的教训。5.3 从App拉起微信小程序和web-view的H5通信这个项目后来确实做了App端于是遇到了另一个需求从App端拉起微信小程序。在uni-app里这个能力是通过uni.launchMiniProgram实现的。但要注意这个API在App端调用时App本身必须已经安装了微信客户端而且要在微信开放平台绑定App和小程序账号不是随随便便就能调的。代码大概是这样uni.launchMiniProgram({ appId: wx1234567890abcdef, path: pages/index/index, extraData: { from: app, userId: userId }, success: (res) { // 拉起成功 }, fail: (err) { // 拉起失败 } })extraData可以在拉起小程序后通过小程序端的uni.getLaunchOptionsSync().query拿到。这里有一个很关键的体验细节从App拉起小程序时一定要带上用户标识信息这样小程序内部可以跳过登录步骤直接使用App侧的会话状态。关于web-view通信这是另一个高频问题。小程序里用web-view加载H5页面H5页面如果想要跟小程序通信不能直接调小程序API必须通过微信官方提供的JSSDK。在H5页面里引入微信JS-SDK后用wx.miniProgram.postMessage发送数据。小程序端通过bindmessage监听// H5页面里 wx.miniProgram.postMessage({ data: { type: backToMiniProgram, payload: { keyword: 塑料瓶 } } }) // 小程序端 web-view 标签 web-view srchttps://h5.example.com/search messageonWebviewMessage/web-view // 方法 onWebviewMessage(event) { const detail event.detail.data[0] if (detail.type backToMiniProgram) { // 跳转到分类结果页 uni.navigateTo({ url: /pages/result/result?keyword detail.payload.keyword }) } }注意postMessage不是实时触发的它的消息是在特定时机比如H5页面返回、分享、销毁、web-view被隐藏才由小程序端接收。如果H5页面需要把消息实时传给小程序同时页面又不关闭那就要考虑用URL参数或URL hash来同步状态小程序端轮询web-view的当前URL来获取最新数据。这个方案虽然有点绕但在复杂场景下确实可用。6. 导出、下载和上传文件处理三件套垃圾分类知识库的运营人员需要定期导出物品清单用户希望把指南保存下来分享给家人管理员需要上传新的分类数据。这几个功能都需要跟文件系统打交道。6.1 导出Excel前端生成还是后端生成产品需求是“用户可以把分类清单保存成Excel发给好友”。最开始我计划直接在后端用PHP的PHPExcel库生成xlsx文件返回下载链接。但后来发现用户当前打开的清单数据量并不大通常不到几百条完全可以在前端直接生成CSV格式。CSV本身不是真正的Excel格式但用Excel打开完全没问题数据量小的时候实用性很高。前端生成CSV的代码很简单function exportCSV(list) { const header [物品名称, 分类, 投放提示] const rows list.map(item [item.name, item.category, item.tip]) const csvContent \ufeff [header, ...rows].map(row row.join(,)).join(\n) const fs wx.getFileSystemManager() fs.writeFile({ filePath: ${wx.env.USER_DATA_PATH}/垃圾分类清单.csv, data: csvContent, encoding: utf8, success: () { wx.openDocument({ filePath: ${wx.env.USER_DATA_PATH}/垃圾分类清单.csv, fileType: csv, showMenu: true }) } }) }这里加\ufeffBOM头是为了防止Excel打开CSV时中文乱码。不加这个Windows上的Excel几乎必乱码这个是老坑了。如果数据量真的很大比如导出全量几万条物品数据前端就会卡顿这时必须后端生成文件返回下载链接。小程序端用uni.downloadFile下载到临时路径再保存到USER_DATA_PATH或者直接用uni.shareFileMessage把文件分享到聊天窗口。我的建议是按数据量切分小于2000条前端CSV大于2000条后端生成Excel并返回临时链接。6.2 下载zip包和本地存储的权限问题还有一个需求是运营后台打包上传一批垃圾分类图片素材用户端可以一次性下载这些素材的zip包。微信小程序下载zip文件的流程是合法的先uni.downloadFile拿到临时文件再用FileSystemManager.saveFile把临时文件保存到USER_DATA_PATH最后解压。等等直接说结论微信小程序没有内置的zip解压API。wx.getFileSystemManager只支持读写、复制、删除文件不支持解压。要解压zip必须引入第三方库比如jszip。在小程序里引入jszip这种库要注意它内部可能用到一些浏览器API比如Blob、FileReader而小程序的运行环境对这些API的支持不完整。实际能跑通的做法是后端把zip包解压好把里面的图片逐张上传到CDN小程序端直接下载图片列表而不是下载zip包。在微信生态里尽量让后端多做一步前端就会少踩十个坑。7. 上线前必看的自查清单从测试版本到审核通过项目功能开发完不代表能上线。微信小程序的发布流程有其特定的规范和坑我把这个项目里踩过的坑整理成了一份自查清单。7.1 测试版本设置、右上角胶囊按钮和加载页测试人员要体验最新版本不需要每次都扫开发版的二维码。正确流程是开发者工具里点击“上传”把代码上传到微信后台的“开发版本”然后在后台“版本管理”里把该版本设为“体验版”再把体验成员的微信号添加到成员管理里。体验成员扫码就能打开体验版和正式版环境基本一致。右上角“三个点”的问题运营同事来问过我能不能关掉。答案很遗憾微信小程序的右上角胶囊菜单是系统级的开发者没办法完全移除。但是可以通过一些方式减少干扰wx.hideShareMenu({menus: [shareAppMessage]})可以隐藏转发按钮。wx.hideHomeButton()可以隐藏首页按钮。自定义导航栏时把页面内容往下压避开胶囊区域让界面看起来更干净。至于“修改刚进入的加载页面”前面说过微信小程序不允许自定义启动图。但可以通过在app.json里设置window的navigationBarBackgroundColor为品牌色让小程序启动时视觉上不突兀同时在首页做骨架屏让“进入后的白屏”变成“模板化占位”这是目前最稳妥的加载体验优化手段。7.2 lazyCodeLoading 和启动耗时微信小程序包体积超过2MB后启动耗时明显增加。解决方式有几个层面分包加载、图片走CDN、去掉不必要的依赖。在app.json里加上一行{ lazyCodeLoading: requiredComponents }开启后小程序会按需注入组件代码不初始化非必需的自定义组件。这个配置对启动性能的提升非常明显尤其是首页不依赖的组件不会再占用启动时间。注意开启这个配置后组件库和自定义组件里的代码必须符合按需加载的要求比如不要在app.vue全局引入某些只在某个页面用的插件。另外上传代码时微信开发者工具会提示“包体积超过限制”。如果真的超了除了分包还能把一些不常访问的页面比如用户协议、隐私政策、运营活动页放进独立分包。独立分包加载时不需要下载主包启动速度更快而且可以单独设置页面路径。7.3 定位相关的坑高德地图和苹果手机位置错误小程序里用了高德地图的选点功能结果苹果手机用户反馈定位不准甚至定位到了旁边省份。排查下来有这么几个原因manifest.json里高德地图的key配置不对。iOS上用高德需要在高德开放平台申请iOS平台的Key绑定的Bundle ID必须和小程序后台的一致否则定位信息就是错的。用户手机本身的“定位服务”没有开启精确位置。iOS 13以上用户可以选择“大致位置”而不是“精确定位”小程序拿到的定位自然不准。可以通过uni.getLocation({isHighAccuracy: true})申请高精度定位但最终还是要用户授权。小程序授权弹窗被拒。如果用户第一次点了“拒绝”之后再调定位API就不会有弹窗了只能引导用户去小程序的设置页手动打开“位置信息”权限。从微信小程序跳转到高德App导航之前实现过一次。视觉上看着很高端其实是调uni.openLocation或者直接调起高德地图App的URL Scheme。注意小程序不能直接调用universal link跳到高德App通常是用wx.openLocation调起“在地图中查看”然后用户自己选择用高德还是其他地图App打开。在小程序生态里能直接调的只有wx.openLocation它底层会拉起手机自带地图或者第三方地图。7.4 审核与隐私保护最后一个大坑是审核。垃圾分类小程序类目建议选“教育 - 教育信息服务”或者“生活服务 生活缴费”不同类目对资质要求不一样。我们第一次提审被拒原因是“涉及用户隐私信息但未配置隐私保护指引”。解决办法是在微信公众平台的“设置 - 服务内容声明 - 用户隐私保护指引”里逐项勾选你用到用户信息的场景微信昵称、头像、位置信息、相册权限等。如果小程序里接入了用户反馈功能一定要在隐私保护指引里声明“用户主动提交的信息”这一项。另外图片识别功能用到了摄像头和相册权限没有在隐私保护指引里声明审核也很容易被打回。这个步骤看起来是纯运营操作但开发阶段就要提前准备否则项目等审核一等就是三四天。还有一点容易被忽略如果用户提交的纠错数据里有垃圾照片而这些照片可能包含用户周边环境信息建议在隐私政策里明确说明这些数据只用于分类纠错并且脱敏存储。我们在后端收到的用户上传图片处理完识别后立即删除原图只保留识别结果和纠错文本既降低存储成本也减少了隐私合规压力。整个项目从启动到上线前后将近两个月。后来又有同行问我做一个垃圾智能分类小程序最大的感悟是什么。说实话技术上没有哪个点是真正“难到做不出来”的真正花时间的全是细节导航栏高度、iOS弹层渲染、setData嵌套字段、文件导出格式、审核类目选择。把这些细节一个个磨过去项目的完成度自然就高了。如果说有什么建议可以留给后续做类似项目的人我的想法很简单先想清楚“用户为什么要用你的小程序”再动手写代码。识别算法也好界面炫酷也好都是手段让用户愿意用、能够信任你给出的每一个分类结果才是这个项目的本质。
返回列表