快递寄件小程序前端实战:核心流程与踩坑记录 1. 项目整体设计与功能全景1.1 寄件小程序的用户场景与核心价值我做这个快递寄件小程序的时候第一件事不是打开编辑器敲代码而是把用户寄一次快递的完整路径自己走了一遍。用户拿起手机可能是电商退货也可能是微信里刚收到一个地址准备寄文件他要做的动作其实非常明确填对地址、选个便宜的快递、约好取件时间、付钱然后等快递员上门。这个小程序要解决的不是“多一个寄件入口”而是让这条路径变得足够短、足够稳。为什么是小程序而不是App因为寄件是低频但刚需的场景用户不会天天寄快递他不想为了一个月一次的寄件去下载一个几十兆的App。小程序即用即走、微信内直接打开、扫一扫就能下单刚好匹配。再加上寄件流程里需要授权手机号、调起微信支付、推送取件通知这些全是微信生态的原生能力在小程序里做可以省掉一大堆跨端兼容工作。前端在这个项目里承担的不只是把设计稿变成页面。寄件流程的每一步都可能中断用户填到一半切走了支付回调没到快递员临时改时间……前端要把这些状态变化全部兜住还要保证页面流畅、数据准确。可以说前端就是寄件体验的最后一公里也是用户感知最深的一公里这块做不好后端再稳用户也感受不到。1.2 前端功能模块划分与工程结构我当时把前端按业务模块拆成了这几个页面和组件结构上尽量保持“一个核心流程一条主线”模块页面核心功能首页pages/index/index寄件入口、价格查询、快递公司列表、优惠券展示下单pages/order/create地址填写、物品信息、比价、预约取件时间订单列表pages/order/list订单分类、下拉刷新、触底加载更多订单详情pages/order/detail物流轨迹、电子面单、客服入口地址簿pages/address/list、pages/address/edit常用地址增删改、智能识别我的pages/mine/index个人信息、常用设置、发票抬头前端工程按 api / components / pages / store / utils / constants 分层。api目录统一封装wx.request所有请求走同一套拦截器统一处理登录态过期、错误码提示components目录只放可复用组件比如地址卡片、快递公司选择项、物流时间轴store用简单的响应式全局对象管理当前订单、地址簿等跨页数据没有上太重状态管理库因为寄件流程的共享状态并不复杂上Redux反而增加心智负担。分包策略也得提前想好。微信小程序主包限制在2MB以内我把首页、下单页、订单列表这三个最核心的页面放在主包地址簿、订单详情、我的页面全部放分包。用户从首页能直接触达的路径必须秒开其他的页面可以等用到了再加载。首发版本如果一股脑全塞进主包审核上线都会很被动。这里多说一句技术选型我最终用了uni-app原因后面单独展开。简单说这套业务除了微信小程序还要在短信链接和公众号菜单里放H5入口一套代码多端编译能省下不少重复工作。2. 寄件核心流程的前端实现细节2.1 下单页从地址输入到订单生成下单页是整个小程序里表单复杂度最高的页面。寄件人卡片、收件人卡片、物品类型、重量档位、预约时间、快递公司列表还要在底部实时展示预估到手价。用户在页面上的每一次选择都可能触发一次价格查询所以这个页面的核心不是“渲染”而是“状态管理”。地址输入是第一个难点。用户很少会老老实实分字段填他们最常见的操作是从聊天记录里复制一整段地址文字直接粘贴进来。这是“地址智能识别”的起源也是我觉得前端能做出的最有体感的功能。实现上不复杂正则提取手机号再用关键词把姓名和地址切分出来// 从剪贴板粘贴的一大段文本中解析地址信息 function parseAddress(raw) { const phoneMatch raw.match(/1[3-9]\d{9}/); const phone phoneMatch ? phoneMatch[0] : ; // 去掉手机号后的剩余文本先过滤干扰词 let rest raw.replace(/1[3-9]\d{9}/, ); rest rest.replace(/快递|寄到|送到|收货|收件人|联系人/gi, ); // 简化版姓名提取取开头连续2~4个汉字 const nameMatch rest.match(/^([\u4e00-\u9fa5]{2,4})/); const name nameMatch ? nameMatch[1] : ; return { phone, name, address: rest.replace(name, ).trim() }; }这个实现看着简单实际坑很多。有人粘贴的文本是“麻烦尽快寄到XX省XX市XX区XX路100号收件人张三电话13800138000”有人是“张三 13800138000 北京朝阳区……”语序完全不固定。我后来给识别规则加了权重手机号前后两个词优先当作姓名剩下内容再按“省市区”关键词做切分识别准确率才从初版的60%提到了85%左右。但即便识别率到85%也绝对不能跳过二次确认。前端要把解析结果渲染成可编辑的表单字段并弹一次确认框让用户自己判断机器识别对不对。凡是用户修改过识别结果就上报一个埋点后续可以持续优化算法这是识别类功能的标准闭环。防重复提交是另一个容易翻车的地方。第一次上线时用户连点两下“提交订单”直接创建出两条一模一样的订单后台多了不少脏数据。后来我在按钮上加disabled加loading页面级再加一个submitting标志请求发起时置true响应回来才复位。页面跳转也做了优化下单成功后不留在原页面用wx.redirectTo跳结果页这样用户返回时面对的不是一张已经提交过的表单。多页面传参同样要谨慎。从地址簿选完地址回下单页一开始我用全局变量存选中项后来发现Android上小程序偶发回收页面导致数据丢失就改成了返回时用eventChannel传值比全局变量稳得多。如果两个页面之间传的是复杂对象用eventChannel还能避免序列化问题。2.2 运费预估与快递比价的前端展示逻辑运费预估是下单页最亮眼的功能也是最容易引发客诉的模块。后端提供价格接口返回各家快递公司的基础运费前端要做的是把用户选的重量、体积换算成后端可计算的价格参数再把多家的价格放在一起做对比引导用户下单。体积重是前端特别容易算错的地方。常规公式是长(cm)×宽(cm)×高(cm)/6000但不同快递公司算法不一样有的除5000有的除8000。我一开始把公式写死在前端后来发现快递公司列表是后端动态下发的每家公司体积重参数还不一样就改成由后端在快递公司配置里返回volumeFactor前端用这个因子计算。前端只需要做一件事跟后端确认单位。用户输入的是厘米传给后端是厘米还是米一定提前对齐否则价格差出一大截。重量选择我用了picker档位而不是input手工输入。档位列表由后端返回1kg以内、1-3kg、3-5kg、5-10kg。为什么不用input因为大部分用户对重量不敏感手工输入反而增加输入成本还会出现“用户填了个不存在重量导致价格估算失败”的情况。档位选择把模糊的东西变成明确选项价格展示更稳定。运费展示一定要把“预估”和“实际”的差异处理好。我在价格卡片右上角固定写“实际费用以快递员称重为准”下单确认弹层里再提示一次。千万不要嫌这个提示啰嗦。用户投诉“价格和实际支付不一致”的根源往往就是前端把预估价写得像最终价。多做一层提示能把大量投诉挡在门外。优惠券的逻辑也可以在这里一并说。订单金额是前端根据运费和优惠券实时算出来的但优惠券列表来自后端每一张券的可使用条件满减门槛、适用快递公司、有效期都不一样。前端不要自己判断券能不能用全部交给后端校验。前端只负责展示“可用券数”用户选中某张券后再请求一次价格接口用后端返回的实付金额刷新页面。这样前端代码简单也不会因为优惠规则改版跟着改。2.3 订单列表页面分页加载与加载更多订单列表是所有电商类小程序都会遇到的页面表面看是列表实际上的问题几乎都出在分页上。如果你去面试小程序岗位“怎么实现列表加载更多”几乎是必考题标准答案就是onReachBottom触底加分页参数控制。核心逻辑很直接data: { orders: [], pageNum: 1, pageSize: 10, hasMore: true, loading: false }, onReachBottom() { if (!this.hasMore || this.loading) return; this.setData({ loading: true }); this.fetchOrders(); }, async fetchOrders() { const { pageNum, pageSize } this.data; const res await request(/api/order/list, { pageNum, pageSize, status: this.data.activeStatus }); const list res.list || []; this.setData({ orders: this.data.orders.concat(list), pageNum: pageNum 1, hasMore: list.length pageSize, loading: false }); }这几个字段一个都不能少。少了hasMore最后一个tab会无限请求少了loading触底事件快速连续触发会并发拉两页出现数据重复或乱序。我的经验是loading和hasMore必须双重拦截缺一不可。tab切换是第二个坑。一开始我切换“全部/进行中/已完成/已取消”时只改了activeStatus没有重置pageNum结果从“全部”切到“进行中”刷出来的数据直接从第3页开始漏了前两页。后来每次切换tab都要把orders清空、pageNum重置为1、hasMore重置为true再重新拉第一页。空状态和错误状态也不能忽略。新用户没有订单时要给一个“暂无订单去寄件”的引导按钮请求失败时要允许点击重试而不是永远停在loading转圈。下单量特别大的用户如果列表一次性渲染几十条出现卡顿可以考虑引入recycle-view做虚拟列表只渲染可视区域内的卡片。不过对寄件场景来说先把分页逻辑写对比上虚拟列表重要得多。3. 关键技术点与多端适配方案3.1 自定义导航栏高度计算与动态标题电商类小程序几乎都会做自定义导航栏因为首页要放品牌头图、搜索框原生导航栏看着太单薄。但自定义导航栏的第一个坎就是高度iOS和Android状态栏高度不一样刘海屏和水滴屏占的像素也不一样直接写死px必然翻车。网上流传很多计算导航栏高度的方案我实测下来最稳的还是用胶囊按钮反推。微信的胶囊按钮位置在每个机型上都会自动适配你只要拿到它的位置和状态栏高度就能算出导航栏的真实高度const menuButton wx.getMenuButtonBoundingClientRect(); const statusBarHeight wx.getSystemInfoSync().statusBarHeight; const navBarHeight (menuButton.top - statusBarHeight) * 2 menuButton.height;这个公式的原理很简单胶囊按钮垂直居中于导航栏所以胶囊顶部到状态栏底部的距离等于胶囊底部到导航栏底部的距离反过来就能推出导航栏总高度。把statusBarHeight和navBarHeight缓存到一个全局对象里所有自定义导航栏页面共用不要在页面里重复计算。自定义导航栏的页面左侧返回按钮要自己画。这里要注意不能无脑navigateBack。如果用户是通过分享链接冷启动进入详情页页面栈里没有上一页navigateBack会直接失败。要做一层判断getCurrentPages().length 1时用reLaunch回首页否则才navigateBack。动态设置标题这个需求来自订单详情页。订单列表点进来导航栏标题要显示“订单详情”但如果运单号上有异常状态标题可以直接改成“运输异常”吸引用户注意。用wx.setNavigationBarTitle在onLoad里根据参数设置一次即可。消息推送场景里用户从订阅消息点进来也可以通过标题把来源信息带上让用户明白自己为什么在这里。3.2 地图选点与定位的权限适配寄件地址有一个高频需求用户到了某个地方想寄东西希望直接定位到当前位置或者在地图上拖一下选个点。微信官方提供了wx.chooseLocation但并不是开箱即用。首先要在app.json里做声明不声明直接调用真机上会fail{ permission: { scope.userLocation: { desc: 用于选择寄件地址和获取当前位置 } }, requiredPrivateInfos: [chooseLocation, getLocation] }这是微信2022年之后的硬性要求很多老项目升级之后定位突然失败基本都是少了requiredPrivateInfos。而且从实际项目来看这个接口在部分个人主体小程序上开通不了需要企业主体动工之前先确认账号资质别等开发到一半才发现接口调不通。授权失败的处理也很关键。用户第一次点了拒绝后面每次调用都会直接fail不能在fail里简单提示一下就完事。要引导用户去设置页重新授权fail: (err) { if (err.errMsg err.errMsg.indexOf(auth deny) -1) { wx.showModal({ title: 需要定位权限, content: 请在设置中开启定位权限后再试, confirmText: 去设置, success: (res) { if (res.confirm) wx.openSetting(); } }); } }地图服务商的选择也要提前定。C端寄件场景我选腾讯位置服务因为微信生态内的key、域名、SDK都无缝衔接。如果项目面向政企、物流园区或者有国测局坐标体系要求可以评估天地图的小程序SDK它提供地理编码和逆地理编码坐标数据符合国内合规要求。不过客观讲天地图的文档和社区相对冷清遇到问题排查成本高纯C端产品不建议首选。地图选点拿回来的经纬度在下单参数里要和地址文本一起传给后端。前端定位不准、地址漂移的问题多半是选点组件拿到的坐标和地址对不上这时候要以后端逆地理编码的结果为准前端不要自己拼地址。3.3 图片上传优化压缩、分片与Worker寄件流程里需要用户上传图片的场景不少物品照片、破损凭证、生鲜打包照片、电子面单截图。一张原图动辄四五兆直接wx.uploadFile传上去弱网环境下用户会在进度条前等到怀疑人生。我的处理顺序是先压缩再分片最后用Worker做耗资源的计算把主线程解放出来。压缩用wx.compressImagequality设成80实测一张5MB的照片能压到1.5MB左右肉眼几乎看不出差别。压缩完再判断是否还需要分片如果单张小于1MB直接wx.uploadFile比分片更快不要无脑分片。这里的原则是“够用就好”。分片上传的思路是这样的用FileSystemManager把图片文件读成ArrayBuffer按512KB一片切开每片调用wx.uploadFile上传带上同一个uploadId和分片序号后端收到后按序号合并。某个分片失败只需要重传那一片不用整个文件再来一遍弱网下体验提升非常明显。Worker在小程序里的作用和浏览器端类似适合做计算密集型的任务。分片之后要给每片算MD5做上传校验如果放在主线程图片一大、分片一多用户会看到页面明显掉帧。我把MD5计算放到小程序Worker线程主线程只负责更新进度条和显示百分比。配置方式很简单先在app.json里声明workers目录然后Worker线程里接收文件数据、返回计算结果// app.json { workers: workers }Worker不是万能的小程序Worker里不能直接调用wx.request它只做计算。所以实际流程是主线程读文件、Worker算hash、主线程负责上传三者配合。别把上传逻辑硬塞进Worker里会直接报错这也是我自己踩过的坑。3.4 支付流程的前端状态管理快递寄件的支付表面上和普通商城没什么区别先让后端下单拿到支付参数再调wx.requestPayment。但前端最容易踩的坑是把支付success回调当成最终结果直接跳转。微信支付的规则是wx.requestPayment的success只代表用户调起支付并输完了密码并不代表商户后台已经收到支付成功的异步通知。尤其在小程序里用户支付完直接杀掉微信、网络闪断后端通知可能迟到甚至丢失。如果前端在success里立刻跳转到“已支付”页用户看到的是成功页后端订单还是“待支付”就会产生大量客诉。稳妥的做法是支付成功后不跳转而是轮询订单详情接口等后端把订单状态更新为“已支付”再跳转。success: async () { // 支付回调后以后端订单状态为准 for (let i 0; i 10; i) { await sleep(2000); const order await request(/api/order/detail, { orderId: this.orderId }); if (order.status PAID) { wx.redirectTo({ url: /pages/order/result?orderId order.id }); return; } } wx.showToast({ title: 支付确认中请稍后在订单中查看, icon: none }); }如果用户中途取消支付fail回调里别一棍子打死。errMsg里包含“cancel”的要区分成“用户主动取消”订单留在待支付状态给用户弹一个“继续支付”的按钮而不是把他打回首页。这里前端的交互要温柔一点因为用户大概率不是不想付只是刚才被什么打断了。实际经验再补充一条requestPayment的timeStamp、nonceStr、package、paySign这些参数前端只要透传千万不要自己拼。后端用什么签名算法、什么随机字符串前端不关心。一旦你自己拼换一个支付渠道就是一次事故。4. 调试联调、版本管理与外部入口4.1 真机抓包调试小程序接口的实操方法做小程序联调最常见的现象是开发者工具里一切正常真机一跑就出问题。这个时候只看日志是不够的你根本不知道wx.request到底发了什么参数、收回了什么响应。我的做法是直接用抓包工具看小程序真实发出的网络请求。这里以Charles为例流程很固定网络上的教程大多讲的是PC端抓包真正抓小程序有几个关键点得注意。第一步电脑开Charles在Proxy菜单里开启SSL Proxying把调试域名的Host加进Include列表。第二步手机连和电脑同一个WiFi在WiFi设置里把HTTP代理改成手动服务器填电脑的局域网IP端口填Charles默认的8888。第三步手机浏览器访问chls.pro/ssl下载并安装Charles的SSL证书。注意iOS装完证书之后还要在“设置-通用-关于本机-证书信任设置”里把证书开关打开否则只能看到一堆乱码请求。这些都做好以后在小程序里点一下“查运费”Charles里就能看到完整的请求报文包括URL、Header、POST body、返回的JSON一目了然。开发者工具的Network面板虽然也能看但真机上的环境、网络、微信版本都和开发工具不一样有些问题只在真机上能复现。抓包特别适合查三类问题一是前端参数名字和后端不一致这种在后端日志里很难看出来但报文里直接对字段就能发现二是线上请求被某层拦截看返回的status code就能定位三是接口反应慢用Charles的Throttle功能模拟弱网看前端loading状态能不能兜住。我再强调一次抓包工具只应该用在自己开发的调试环境里这是基本的职业边界。说一个真实案例。上线后有人反馈“预估价12元下单却扣了18元”看起来像后端乱收费。我用Charles抓了下单请求发现前端传给后端的volume字段一直是0原因是下单页的体积输入框绑定错了字段bindinput绑到weight上了重量倒是传了体积丢了。后端没有体积只能按默认体积算价格自然对不上。这个bug如果不抓包在代码里排查可能要半天。4.2 体验版分发、强制更新与冷启动刷新小程序开发完成之后第一步不是提交审核而是发体验版让同事试。微信开发者工具点“上传”后小程序后台会出现一个版本可以把它设为体验版然后在“成员管理”里把测试同事的微信号加为体验成员。体验版不是谁都能打开的没加体验成员的人扫码只会看到无权限。如果只是临时测一下可以在开发者工具里点“预览”生成一个带有效期的二维码。但正式收集几天试用反馈还是要走体验版不然每次都要重新生成二维码对方过几个小时再点就失效了。收集反馈这件事我强烈建议发给同事之前先给一个反馈模板设备型号、微信版本、操作步骤、截图。“打不开”和“白屏”这种纯描述没有任何排查价值只有加上环境信息前端才能快速定位问题。我自己做一个寄件小程序版本评审的时候会顺手拉一个小程序体验群让测试人员按“机型操作路径截图”格式反馈收集上来的信息质量完全不一样。版本发布上线还有一个细节容易被忽略小程序有缓存机制用户可能一直停留在旧版本上。如果线上版本出现问题想让用户尽快升级用wx.getUpdateManagerconst updateManager wx.getUpdateManager(); updateManager.onUpdateReady(() { wx.showModal({ title: 更新提示, content: 新版本已准备好是否重启应用, success: (res) { if (res.confirm) updateManager.applyUpdate(); } }); });配合版本号做强制刷新后端在下发配置时带一个apiVersion前端启动时比对本地storage里的版本号不一致就弹窗提示“版本已更新请重启小程序”。这个机制在接口做了破坏性变更时特别有效可以避免旧版本客户端带着旧参数请求新接口产生一堆脏数据。H5唤起小程序这个需求在寄件场景里也经常出现短信通知里放一个H5链接把用户导到小程序里领券或下单。实现方式是用微信开放标签wx-open-launch-weapp需要绑定JS接口安全域名还要走微信JS-SDK签名。最常见的失败原因是域名没加到公众号的“JS接口安全域名”里或者签名用的URL和实际访问URL不一致。排查时先看JS-SDK的ready事件有没有触发再看开放标签的dom节点是否存在这两个点能过滤掉大部分问题。4.3 uni-app打包小程序与原生方案怎么选写前端之前技术选型是最容易纠结的一步。快递寄件小程序我用的是uni-app原因很简单这套业务除了微信小程序还要有H5版本方便放在短信链接和公众号菜单里。如果只做微信端我可能会选原生但要覆盖多端uni-app一套代码省下来的时间非常可观。用uni-app开发微信小程序有几个打包期和运行期的问题必须提前知道。第一单位。微信小程序用rpxuni-app在H5端是px在小程序端编译成rpx。开发时如果直接用px在某些机型上会明显偏小。建议写样式统一用rpx涉及动态计算的尺寸用uni-app的工具函数换算。第二生命周期。uni-app保留了onReachBottom、onPullDownRefresh这些页面生命周期但必须写在页面的配置里不能写在组件里。我第一次把onReachBottom放在一个订单卡片组件里结果触底事件永远不触发排查了一个小时才意识到。第三easycom是uni-app的亮点组件不用手动import放进components目录就能用。但它对组件文件名的目录结构有约定不按约定来容易引入失败报错还比较隐晦遇到“组件未注册”的报错先检查目录命名。原生方案的优势是性能和可控性微信新接口上线时原生永远最先支持社区调试资料也最全适合团队只服务微信单一渠道、不想引入框架学习的项目。我整理了一个简单的对比对比点uni-app原生小程序多端复用一套代码编译多端每端单独开发学习成本需懂Vue语法只需小程序语法新接口支持依赖框架更新微信发版即用包体积框架注入增加主包相对更小社区生态uni-app生态较全微信原生文档最全没有哪个方案绝对好核心看团队规模和你到底要覆盖几个端。如果你现在还不确定未来要不要做App或H5选uni-app寄件这种表单型业务完全够用真到了要追求极致性能的那天再做局部原生化也不迟。5. 高频踩坑点与排查建议5.1 常见问题速查表开发快递寄件小程序的过程中我把高频问题整理成一张速查表给团队新人也发了一份这里直接贴出来基本是每个模块的真实血泪现象可能原因解决建议订单列表触底后重复请求onReachBottom触发频率高loading和hasMore没有互斥每次请求前做if判断请求完成再复位自定义导航栏被刘海屏遮挡直接用固定高度用胶囊按钮坐标状态栏高度反推支付成功但订单仍是待支付依赖前端success回调以后端异步通知为准支付后轮询订单状态上传大图进度卡在99%最后一个分片失败没有重试机制分片失败单独重传不要从头传wx.chooseLocation真机fail缺少requiredPrivateInfos声明app.json补配置并确认主体资质体验版发给别人打不开对方不在体验成员列表或二维码过期后台添加体验成员正式收集反馈用体验版H5唤起小程序失败域名未加白名单或签名URL不一致核对JS接口安全域名和当前URL页面间传参老是undefined参数含中文未encode或字段名大小写不一致统一用encodeURIComponent传参取参后decode这张表看着简单但每一个条目背后都是一个真实事故。建议新人接手这类项目时先把表里对应的代码自查一遍能少踩一大半的坑。5.2 针对寄件场景的几个边界处理建议通用问题之外寄件场景还有一些非常“业务”的边界情况前端很容易漏我这里单独拎出来讲。第一下单地址的二次确认。用户从聊天记录粘贴的地址识别准确率再高也不能直接信任。前端要做的是把识别结果解析成可编辑的表单字段并弹一次确认框把“自己填”和“机器识别”区分开。用户修改过识别结果的情况要上报埋点方便后续优化识别算法这块不能省。第二预约取件时间跨天。用户晚上11点下单预约“明天上午”和预约“今天下午”已经是两个自然日。前端的时间选择器要基于后端返回的营业时间动态生成可选时段不能写死“上午9:00-12:00”这种固定选项。节假日、网点休息日也要跟着后端配置走否则用户会约到一个根本没人的时间。第三支付环节的异常恢复。用户支付成功后杀掉小程序冷启动进入首页不应该只看到一个孤零零的待支付订单。我加了一个恢复逻辑每次冷启动都查询最近30分钟是否有“支付中”的订单有就弹窗问用户“是否查看订单进度”。这个小功能上线后客服量降了不少用户也不会因为找不到订单而焦虑。第四登录态过期。寄件流程中任何一步接口返回401都不要只弹一个“请重新登录”就完了。要把用户要做的下一步操作暂存下来重新登录成功后自动回到原流程。不然用户重登之后发现刚才填的地址全没了大概率直接放弃下单。这类小细节对寄件转化率的长期影响非常明显。这个快递寄件小程序做到后面我自己最大的体会是前端真正难的地方从来不是把页面画出来而是把各种状态和时机管住。支付成功不等于支付成功点击一次不等于只能提交一次用户切个后台再回来页面要像什么都没发生过一样接着走。这也是为什么我一直觉得做小程序前端的人一定要对onShow、onHide、冷启动这些生命周期有肌肉记忆它们比任何组件库都重要。如果这篇文章能帮你少走几个弯路或者面试时多了几个能打的实操点那就不算白写。最后再分享一个我个人坚持的习惯每次发版前用真机把寄件主流程完整跑一遍扫码进小程序、下单、支付、看物流、杀进程重进。寄件这件事用户不关心你用了什么架构、分了几个包他只关心今天这个包裹能不能顺利寄出去。前端要做的就是把所有不确定挡在用户看到之前。