这几年做小程序,后台代码写腻了,半夜还在帮朋友调包体积的,十个里面八个都在纠结同一件事:框架到底怎么选。我做过原生,用 uni-app 趟过混合开发,也帮人维护过 Taro 的老项目,三种风格都摸过一遍。今天这篇不是官方文档翻译,就是把“微信小程序开发框架”这条路从头到尾捋清楚——成熟的项目架构长什么样、主流技术栈各自有几斤几两、真正选型时看哪些硬指标,以及那些你翻遍文档也找不到的坑。不管你后面是想做校园外卖、社区团购、菜谱工具还是门店系统,只要牵扯到小程序,这篇文章应该能帮你少走一大段弯路。
1. 微信小程序开发框架全景:从原生到跨端的变迁
1.1 原生小程序开发到底哪里不行
很多刚入门的同学会问:直接用微信官方那套不就行了?说实话,几年以前我也是这么干的,当时觉得 WXML 有点像 HTML、WXSS 就是 CSS,写起来应该很快。结果真上一个功能完整的项目,痛点就全冒出来了。
首先,原生的语法和主流前端差得多。没有真正的组件内样式隔离,没有完善的组件化开发模式,更没有现代前端熟悉的响应式数据驱动。写页面的时候是data里放一堆变量,然后通过setData改,改完页面重新渲染,这套思维对刚从 Vue/React 转过来的开发者来说非常别扭。
其次是复用性差。一个页面上抽出的公共逻辑,想在另一个页面复用,大概率是复制粘贴。公共组件是有的,但用起来比较原始,属性传递、事件派发都需要手写大量模板代码。做一个有十几个页面的业务小程序时,这种代码库会很快变成一座“无从下手”的泥山。
还有就是工程化能力弱。原生小程序没有成熟的 npm 生态接入方案,虽然微信开发者工具后来支持了 npm,但实际体验依然很生硬。单元测试、CI/CD 集成这些前端工程化的基础能力,在小程序原生体系里属于“有但不好用”。对一些小团队、小项目来说可能无所谓,但一个稍微正经点、要长期迭代的产品,原生这一套到后面就会很吃力。
原生最大的优点是性能和稳定性。它毕竟是官方亲生的,每次新功能发布都是第一时间支持,运行时坑也少。但付出的代价就是开发效率偏低、代码组织能力弱。所以才有那么多第三方框架冒出来,想办法把 Vue 和 React 的方言带到小程序生态里。
1.2 主流开发框架盘点与定位
我按时间线和使用习惯,把小程序框架分成了几个梯队。讲结论之前先列个表,方便大家对照:原生、uni-app、Taro、mpvue、WePY 这几个我都用过或维护过,最关键的区别在语法、跨端能力、维护状态上。
| 框架 | 语法风格 | 跨端能力 | 当前维护状态 | 适合人群 |
|---|---|---|---|---|
| 原生(微信官方) | 自定义 WXML/WXSS | 仅微信,支付宝/百度需重写 | 官方持续更新 | 追求极致性能、仅需微信端 |
| uni-app | Vue 2/3 语法 | H5、App、各家小程序 | DCloud 较活跃 | 熟悉 Vue、想低成本多端 |
| Taro | React 语法(也支持 Vue) | 小程序、H5、React Native | 京东开源,社区活跃 | 熟悉 React、需要跨端 |
| mpvue | Vue 2 语法 | 微信、支付宝、H5 | 基本停更 | 老项目维护 |
| WePY | 类 Vue 语法 | 微信为主 | 基本停更 | 老项目维护 |
说明一下,mpvue 和 WePY 当年算是跨端框架的探路者,放在今天已经不太建议新项目去选。我曾经给一个 WePY 的老项目做过维护,最大的感受是:它把开发者带到了 Vue 写法里,但底层编译器的更新跟不上微信小程序的升级速度,很多新接口用不上,出问题只能等社区各路大神打补丁,非常被动。
真正在 2024 年能被认真考虑的,其实就是原生、uni-app、Taro 这三个大方向。后面我会重点围绕这三者展开。
1.3 核心设计思路:编译时转换与运行时适配之争
搞懂一个框架,先看它底层怎么把小程序的页面代码“编译”出来。这里有两个主流技术路线,我平时给朋友讲的时候喜欢用装修打比方。
编译时转换的思路,相当于设计师画完效果图,直接把它翻译成施工图交给工人照做。框架读到你写的 Vue/React 模板,分析其中的结构和绑定关系,然后生成对应的 WXML 和 JS 文件。uni-app 在很长一段时间内都是走这个路线,优点是最终产物更接近原生,运行时的额外体积小、性能损耗低;缺点是模板语法会被限制,有些写惯了的地方没法直接用。
运行时适配的思路则是在小程序内部塞进去一个“解释器”或称运行时容器,代码进来之后,通过虚拟 DOM 或一套数据驱动引擎,再映射成小程序的界面。Taro 3.x 就是这么做的,它允许你在项目里使用完整的 React 生态,理论上语法自由度更高。但代价也很明显:运行时库本身就占体积,页面复杂时 setData 频繁,性能也有损耗。
这两条路没有绝对的好坏。追求极致性能和冷启动速度,那编译时方案值得优先测试;如果团队希望写代码的过程更爽更自由,且对包体积不敏感,那运行时方案也没问题。我见过一个小程序工具应用,用了运行时适配的框架后,首页复杂筛选列表交互依旧很顺,关键还是在项目规模和架构设计上有没有提前做功课。
2. 架构设计解析:小程序工程化与模块化
2.1 小程序的运行时架构:逻辑层与渲染层分工
很多人写小程序从来不关心它内部是怎么跑的,但一旦性能出问题,连排查方向都没有。小程序的运行环境其实分成两个隔离的世界:逻辑层运行在 JavaScript 引擎中,负责处理数据、生命周期、网络请求等;渲染层运行在 WebView 里,负责把页面画出来。两层之间的通信靠系统提供的 JSBridge,而最常见的通信方式是setData。
这里有个非常容易踩的性能误区:setData本质上是一次序列化加跨层通信。你要是把它当成普通前端赋值的this.data = something来用,频繁地给渲染层塞大对象,页面就会肉眼可见地掉帧、卡顿。打个比方,逻辑层像是老板,渲染层像是设计师,setData就是老板喊话传需求,喊的次数太多、每次传的图纸太大,设计师那边自然容易卡死。
所以在架构设计阶段就要立下规矩:数据能不传给渲染层就不传;页面里展示用的字段尽量精简;大数据列表必须做分页;地图、动画这类高频更新的场景,专门做节流处理。这些原则和用哪个框架无关,但框架本身会影响你能用多少手段去控制它。原生有一套自己的优化路径,uni-app 和 Taro 也各自提供了相应的优化 API,选型之前先把这套底层模型聊透。
2.2 项目目录结构:一套可以直接抄的分层方案
架构这种东西,听一百遍不如看一套模板。我根据自己给中小型电商和团购项目搭结构的经验,分享一个被验证过好多次的目录方案。它同时适用于 uni-app 和 Taro,原生项目也可以参考。
src/ ├─ pages/ # 页面目录 │ ├─ index/ # 首页 │ ├─ order/ # 订单相关 │ └─ ... ├─ components/ # 公共组件 │ ├─ goods-card/ │ └─ ... ├─ api/ # 接口请求封装 │ ├─ module/ │ └─ request.js ├─ store/ # 全局状态 ├─ utils/ # 工具函数 ├─ static/ # 静态资源 ├─ styles/ # 公共样式 └─ config/ # 环境配置核心思路是“页面和业务逻辑分离、组件和页面分离”。pages下面每个文件夹只放页面本体,components放跨页面复用的东西,api按照业务模块再做二次封装。这样最直接的好处是:新同事入职看代码时,不用翻半天猜这个函数是干嘛的;要换后端接口时,只动api目录,不会波及页面逻辑。
另外强烈建议在项目启动初期就把环境配置拆出来。开发版、体验版、正式版用的接口地址、AppID、统计 ID 都可能不一样。我见过无数个项目把 baseURL 硬编码在几十个文件里,上线前改配置改到崩溃。放到config目录统一管理,通过构建环境变量去切换,是性价比最高的一件事。
2.3 状态管理选型:globalData、Vuex、Pinia、MobX
状态管理在小程序里是个容易走极端的话题。小项目用全局变量就能跑,大项目用各种状态库也会踩坑,全看怎么匹配场景。
原生小程序自带一个globalData,说白了就是一个全局对象,任何页面都可以读写。用它来存登录态、用户基本信息这类读多写少的数据没什么问题。但如果是购物车这种多页面共享、修改频繁的数据,再用globalData就很容易失控:页面 A 改了,页面 B 不知道,界面不同步,查 bug 时大脑要同时维护几十个隐含状态。
Vue 技术栈下建议直接进 Vuex/Pinia,优点是有完整的响应式体系和调试工具。Pinia 相比 Vuex 更轻、TypeScript 支持更好,uni-app 新项目推荐直接用 Pinia。React 技术栈下用 Redux Toolkit 或 MobX 都可以,Taro 3 对这两者的支持都不错。MobX 的写法更接近“可变状态”,写起来快,但项目大后心智负担偏重;Redux Toolkit 样板代码多一点,可预测性和调试体验更好。
我个人的经验法则是:项目里有超过两个页面需要共享同一个状态,且这个状态修改频率高于“每次打开页面拉一次”,那就值得引入状态管理库;否则宁可用简单的实例方法或事件总线。状态管理本身也是复杂度,不要为了“别人都用了”而去硬上。
2.4 组件体系:自定义组件与公共组件库
组件化是前端开发的老话题,在小程序里却经常被忽略。原生的自定义组件用Component()构造器,通过properties接收外部传入的数据,通过triggerEvent抛事件给父组件。这套机制本身没问题,就是用起来繁琐,每次都要手写一堆配置。
跨端框架里,组件开发体验会好很多。如果你用的是 uni-app,可以像写 Vue 组件一样写一个.vue文件,父组件用:prop传值、@event监听。如果是 Taro,就按 React 风格写.jsx/.tsx。开发效率上确实提升明显,但要注意跨端框架的组件并不完全等价于原生组件,尤其是一些特殊 UI 需求,比如地图、视频、画布,建议还是优先调用官方组件再包一层。
还有一个细节:跨端框架引入第三方 UI 库时要非常谨慎。市面上成熟的 UI 库大多对特定框架做了定制,比如 uView 只用于 uni-app,Taro UI 专门服务 Taro。如果你拿一个小程序原生组件库硬塞进 uni-app 项目,大概率会出现样式错乱、事件失效的问题,最后还得乖乖换掉。选组件库之前,先确认它支持的目标框架和版本。
3. 主流技术横向对比:选框架时的几把标尺
3.1 开发效率与上手难度:Vue 和 React 的偏好之争
选框架首先看的是团队技术栈,这是最实际的。团队全员写 Vue,硬上 Taro 的 React 风格,学习成本远高于框架带来的收益。反过来说,React 团队转 uni-app 也会有一股说不出的别扭。
单看上手速度,我个人觉得 uni-app 门槛稍微低一点。Vue 的模板语法看着亲切,数据绑定直观,遇到问题搜资料也方便。Taro 如果选 React 风格,JSX 的写法一开始会让传统 Vue 开发者不太习惯,但你对 React 熟悉的话,基本就是零成本切换。
这里还要提一嘴,uni-app 对 Vue 3 的支持已经比较成熟了,组合式 API 写起来很舒服;Taro 3.x 也支持 Vue 3,但社区主流例子还是 React 居多。建议先在官方示例模板上各写一个页面感受一下,别光听别人吹。
3.2 构建产物与包体积控制:被热议的 2MB 限制
包体积问题是我这次想重点讲的部分,因为很多人在项目中后期被它坑得措手不及。微信小程序早期确实有主包不大于 2MB 的限制,平时用的打包工具报错里也常见 “source size exceed max limit”,uni-app 打包后的产物往往比原生大,因为它会把运行时内核塞进去。
报错信息类似这样:
source size 2612kb exceed max limit 2mb看到这个不要慌,先定位到底是谁在占空间。我的排查顺序一般是:先看unpackage或dist目录的产物结构,再按体积大小排序,找到最大的几个文件。绝大多数情况下罪魁祸首是:全部引入了 UI 组件库、把图片当本地文件打包、没有用分包、没开代码压缩。
解决手段也很有套路。第一是改为按需引入组件,比如 uView 之类的库可以只引入用到的组件模块,能省下好几 MB 的产物。第二是图片全部换 CDN 地址,不让图片占用本地代码包。第三是配置分包加载,把非首页功能放进subPackages,主包只保留核心页面。
微信后来其实放宽了总包限制,但主包大小仍然是硬指标,直接在开发者工具里面可以看到具体警告。与其等上线前夕再来加班,不如一开始就把“能分包就分包、能按需就按需”写进代码规范。
3.3 跨端复用与生态:uni-app 和 Taro 的正面交锋
uni-app 和 Taro 最常被拿来对比。uni-app 的优势在于“一套代码,多端运行”,它支持 H5、iOS/Android App,还有微信、支付宝、百度、字节等各家小程序。Taro 则主打小程序 + H5 + React Native,它由京东开源团队维护,社区里大厂项目很多。
生态上,uni-app 有一个很大的插件市场,里面有大量现成的模板、组件和示例项目,拿过来改改就能用。但这也带来一个问题:插件质量参差不齐,有的年久失修、上个微信版本还能用,下个版本就报错了。看插件时我习惯先看最近更新时间、星星数量,以及 issue 区的活跃度。Taro 的插件市场相对克制,但官方提供的 Taro UI 组件库在设计上更统一。
有跨端需求的项目,uni-app 的综合胜率更高,特别是你要做 App 的时候,它的编译速度也比自己维护两套码快。但假如你本来就在 React 技术栈里,而且业务核心场景是微信小程序为主,Taro 写起来会更顺手。
3.4 选型决策清单:不同场景下的最终建议
如果把这些经验浓缩成一张决策清单,大概是这样的:
- 只瞄准微信端,追求极限性能,团队能接受原生开发的学习曲线,选原生。
- 熟悉 Vue,想低成本覆盖小程序 + H5 + App,选 uni-app。
- 熟悉 React,预计多端发力,且愿意接受运行时适配带来的体积代价,选 Taro。
- 项目是长期维护的复杂管理系统,不建议用 mpvue/WePY,哪怕在维护也建议早日规划迁移。
- 项目简单到没多少业务逻辑,只有几个表单页,原生反而最干脆,引入框架是负优化。
4. 实操过程与核心环节实现:社区团购小程序的落地
4.1 项目初始化:用 uni-app 快速搭建
我们用 uni-app 演示一个社区团购小程序的搭建过程,正好也能把前面讲的理论串起来。首先在 HBuilderX 里新建项目,选 Vue 3 模板,填上微信小程序的 AppID。也可以用命令行版:
npx degit dcloudio/uni-preset-vue#vite my-tuan-gou cd my-tuan-gou npm install npm run dev:mp-weixin启动后,生成的dist/dev/mp-weixin就是小程序代码。在微信开发者工具中导入这个目录,微信 AppID 保持一致,就能看到页面渲染出来了。
目录结构方面,按照前面分享的分层方案来创建:pages下放首页、分类、购物车、订单等页面,api下封装商品列表和订单接口,store用 Pinia 管理购物车状态。项目里额外引入一个轻量 UI 组件库按需引入,避免包体积爆炸。
4.2 页面列表加载更多:分页逻辑与触底加载
社区团购的核心页面是商品分类列表。这里涉及一个高频需求:滚动到底就自动加载下一页。微信小程序原生的onReachBottom在 uni-app 中是页面生命周期函数。写一个商品列表的 Vue 3 组合式函数:
import { ref } from 'vue'; export function useGoodsList(fetchApi) { const list = ref([]); const page = ref(1); const pageSize = 10; const finished = ref(false); const loading = ref(false); async function loadData(refresh = false) { if (loading.value) return; if (refresh) { page.value = 1; finished.value = false; list.value = []; } if (finished.value) return; loading.value = true; try { const res = await fetchApi({ page: page.value, pageSize }); list.value.push(...res.data.items); if (res.data.items.length < pageSize) { finished.value = true; } else { page.value += 1; } } finally { loading.value = false; } } return { list, finished, loading, loadData }; }页面上通过onReachBottom触发下一步加载,同时在不满足触底条件时提供一个按钮作为兜底。个人经验:分页接口一定要有page和pageSize两个参数,返回数据里最好附带total,前端用list.length >= total判断是否到底,比用返回列表长度判断更准确。
4.3 顶部导航栏高度计算与刘海屏适配
“微信小程序顶部导航栏高度”这个热搜词,说明很多人被这一块折腾过。小程序默认的导航栏高度由系统决定,不同机型差距很大,特别是在 iPhone 的刘海屏和安卓各种异形屏上。如果你要做自定义导航栏,必须动态获取状态栏高度和菜单按钮位置。
核心代码大概是这样的:
function getNavBarInfo() { const sysInfo = uni.getSystemInfoSync(); const statusBarHeight = sysInfo.statusBarHeight || 20; let menuRect = null; if (uni.getMenuButtonBoundingClientRect) { menuRect = uni.getMenuButtonBoundingClientRect(); } if (menuRect) { const navBarHeight = (menuRect.top - statusBarHeight) * 2 + menuRect.height; return { statusBarHeight, navBarHeight, right: menuRect.right, top: menuRect.top }; } return { statusBarHeight, navBarHeight: 44, right: 0, top: statusBarHeight }; }拿到statusBarHeight和navBarHeight后,给自定义导航栏容器设置padding-top,再根据菜单右侧位置动态计算标题和按钮的布局。这套代码我基本是复制到每个需要自定义导航栏的项目里,省事可靠。有一点要注意:uni.getMenuButtonBoundingClientRect是微信小程序才有的接口,跨端跑 H5 时要做兼容判断,否则会报错。
4.4 登录流程与用户身份整合
社区团购必须要登录。微信小程序最基础的登录流程是wx.login获取code,把code交给后端,后端再去微信官方接口换openid和session_key。在 uni-app 里可以直接用uni.login:
async function loginFlow() { const loginRes = await uni.login({ provider: 'weixin' }); const { code } = loginRes; const userRes = await api.loginWithCode(code); // userRes 里拿 token 和 openid,存到本地 storage uni.setStorageSync('token', userRes.data.token); }一个经常被忽视的问题是:不要每次启动小程序都走完整登录流程。用户打开页面就调用uni.login换 token 会白白浪费后端资源,而且用户如果长时间不操作,token 过期再来做静默续期即可。更合理的做法是:首页接口先请求,如果返回 401 再做登录,登录成功后把新 token 同步回上一次失败的请求。
网页端同步小程序微信登录,很多人一开始会以为只要在网页里调wx.login就行,其实是两套体系。网页端用的是公众号网页授权(openid还是要从后端拿),它和小程序的code登录并不通用。比较常见的统一登录方案是:让小程序和网页分别把自己的openid/unionId传给业务后端,后端在自家用户体系里做关联绑定。
4.5 功能细节:自定义单选框、监听离开小程序与防截屏
接着聊几个项目里大概率会遇见的细节需求。
单选框在小程序原生组件里样式很丑,写美观的自定义单选是必学技能之一。我的常用思路是:把原生radio或checkbox隐藏,用 View 渲染一个圆点图标,监听点击事件后更新选中状态,再用一个变量记录当前选中值。在 Vue 里可以写成一个简单的组件,父组件传options和v-model来复用。
监听用户离开小程序,可以用onHide和onShow生命周期。注意onHide不只代表退出小程序,跳转到另一个小程序或打开公众号时也会触发。我就在一个拼团项目里遇到过这样的坑:用户切到微信聊天再切回来,页面状态被onHide里的逻辑清掉了,导致回来要重新加载。正确做法是判断小程序切后台还是跳转场景,可以通过路由来源做更细的区分。
防截屏是另一个比较偏门的需求。iOS 从某个版本开始支持检测截屏事件,但小程序里的实践体验并不算好。可靠的方案是:用原生插件或 web-view 加载一个受 DRM 保护的内容页,但成本高、跨端难。如果我们只是做一个隐私提示、禁止长按保存图片这类的“软防截屏”,在小程序里用 CSSuser-select: none再加遮罩水印就能达到一定效果。想完全阻止截屏,坦白说很难依赖纯小程序技术栈搞定。
5. 常见问题与排查技巧实录:踩坑笔记
5.1 uni-app 打包超限 2MB 的完整解决过程
开头提到的 2MB 超限几乎是跨端框架新手的定番关卡。我有一次把社区团购项目打包,报错信息赫然写着:
source size 2612kb exceed max limit 2mb当时第一反应是删图片、压缩代码,但效果有限。后来静下心来分步排查,才把体积降了下来。
第一步,看包内容占比。打开dist产物目录后,发现uni_modules里的一个轮播组件就占了几百 KB。搜了项目代码,发现好几处其实根本没用到。删除无用组件、强制按需引入后,体积直接掉了一截。
第二步,把本地图片全部搬上 CDN。项目里有一些商品占位图、图标,打包时会被转成 base64 或者直接复制进包。把这些图片统一替换成 CDN 地址后,包体积立刻又小了不少。
第三步,配置分包。把pages.json里非首页、非 tab 页面全部移到subPackages,主包只保留底部 tab 对应的页面和公共资源。注意,tabBar 页面没法放进分包,所以尽量别把 tab 页面做太多功能。分包配置完成后,主包体积基本就会落在红线以内。
最后还要提一句,微信原生项目也适用这套排查逻辑,不是只针对 uni-app。
5.2 微信开发者工具里的小程序怎么发给其他人试用
团队开发、给客户验收时,很多人会卡在“怎么把开发者工具里的小程序发出去”。其实有几种方式可以组合用。
预览功能最简单。在微信开发者工具点击“预览”,会生成一个二维码,扫码即可在手机微信里打开开发版小程序,但只有授权开发者才能扫。体验更完整的是“体验版”模式:先把代码上传到微信公众平台,然后在后台成员管理里添加体验者,体验者扫码即可打开体验版。这种方式适合给非开发者身份的运营、客户试用。
如果只是想收集试用的反馈,我建议在体验版基础上内置一个反馈入口,比如在设置页放一个“意见反馈”按钮,直接调起客服会话或打开一个反馈表单页面。收集来的反馈会比微信聊天里零散的消息好整理太多。给外界试用时还要注意权限控制,体验版的有效期和可体验人数都是有限制的,别把体验二维码公开到未知环境里。
5.3 H5 唤起微信小程序链接无法访问的原因
“H5 唤起微信小程序链接无法访问”也是高频搜索关键词,很多人第一次接触场景值就被绕晕了。H5 页面要唤起小程序,正常流程是后端生成 URL Scheme 或 URL Link,前端赋值给<a>标签或window.location。用户点击后,微信会根据不同的环境去打开对应的小程序指定页面。
遇到“无法访问”,常见原因有三个。第一个是业务域名校验不过。H5 所在的页面域名必须在微信公众平台配置为业务域名,否则微信会拦截跳转。第二个是 URL Scheme 的过期时间很短,试了很多次之后容易失效。第三个是场景值权限问题,有些方式只适用于特定的小程序类型或特定场景,比如部分页面仅可在 App 内打开。
排查时先看后端返回的错误码,我曾经遇到过错误码提示10002,最后查出来是生成请求里缺少了path或query引起的。先把文档看准,再逐项对照参数,比瞎猜效率高很多。
5.4 使用 Charles 抓包排查小程序数据交互
搞不懂小程序接口返回了什么,前端和后端扯皮的现象太常见了。Charles 是我常用的本地排查工具之一,用在开发调试上非常顺手。
流程不复杂:电脑和手机连同一局域网,电脑上打开 Charles 并开启 SSL Proxying,手机上设置 HTTP 代理指向电脑 IP 和端口,再去安装 Charles 的根证书。然后手机里打开小程序,Charles 就会列出小程序发出的 HTTPS 请求,可以看到请求头、参数和返回结果。要注意的是,因为小程序有域名校验,Charles 默认只能抓到经过它代理的流量,别开着系统全局代理模式到处乱试,会产生大量干扰请求。
实际上,我更推荐用微信开发者工具自带的 Network 面板看请求,更快捷、更干净。Charles 的价值在于真机调试、模拟弱网、查看某些不在开发者工具里出现的场景流量。不要依赖它做任何“绕过”操作,它就是一个普通的 HTTP 抓包诊断工具。
5.5 天地图、蓝牙定位等场景的实操提醒
接着再补几个我在项目里实际处理过的场景问题。
天地图在小程序里集成,很多是在做地图路线、选址类的业务。常见坑是:天地图的服务接口在小程序里会涉及跨域和域名白名单,需要去申请对应的 key,并把接口域名加到 downloadFile/request 合法域名里。地图组件本身建议优先用微信原生 map 组件,再通过第三方 JS 库去绘制特定图层。
蓝牙定位的坑更典型。小程序里做蓝牙连接,要先关注wx.openBluetoothAdapter的初始化时机,尤其在 Android 端,需要先确认手机蓝牙已打开,再调用初始化,否则容易直接报错。另外,蓝牙设备的扫描、连接、读写都是异步事件,必须用回调或 Promise 包装好,不然会出现“扫描到设备但连接不上”“连接上了但写数据失败”这类难以复现的诡异问题。
6. 延伸思考:从示例到行业级小程序架构
6.1 社区团购/校园订餐的行业架构参考
社区团购和校园食堂订餐,是很多朋友练手的热门方向,也是每次热搜都绕不开的项目类型。这类业务其实可以抽象成一套非常标准的话术:用户浏览商品、选品下单选地址、支付、然后商家接单、骑手配送或到店自取。
对应的模块拆分大概是这样的:
- 用户端小程序:首页展示、商品分类、购物车、下单结算、订单列表、客服会话。
- 商家端:接单看板、库存管理、打印小票。
- 配送端:抢单、路线导航、送达确认。
- 管理后台:商品管理、订单管理、用户管理、数据看板。
从架构上看,用户端小程序要尽量避免一个巨型页面“从头到尾”承载所有业务,商品列表、购物车、订单详情最好都各自拆成独立页面和独立状态。前后端接口设计上,要明确幂等性,尤其付款回调这类核心链路。我见过很多新人在下单选地址这一环节踩坑,地址定位不准、库存扣减不一致,这些最好在项目开始时就用流程图梳理清楚。
6.2 菜谱/内容型小程序的产品和性能要点
“香哈菜谱”这类内容型小程序,核心讲究的是信息流体验和图片性能。用户刷到的是大量图文卡片,如果每张图都从服务器拉大图,流量和加载速度都会非常憋屈。常规做法是:列表接口返回压缩后的缩略图,点击看详情时再加载高清图;图片都放在 CDN,并且配置合适的缓存策略。
小程序里的图片懒加载也很关键。原生image组件支持lazy-load属性,uni-app 里同样可以传这个属性。由于列表页很长,懒加载能明显减少初始渲染压力。做像我前面那样的触底分页加载时,也要注意避免数据叠加后 DOM 节点过多导致的卡顿,可以适时做虚拟列表或者减少一次加载的条数。
6.3 工具类/游戏类小程序的特殊坑
其他类型的应用也有不少独特点。如果做工具类小程序,比如表单填报、计算器、图片合成,核心是注意力集中在交互反馈上。如果做游戏类小程序,则要注意 canvas 的性能优化,比如不要把逐帧动画用setData推动,而是直接操作 canvas 的绘图上下文,画面才能流畅。
还有一类被忽视的问题是 iOS 和安卓的差异。同样的wx.getSystemInfo在不同端的字段不完全一致,蓝牙、剪贴板、相册权限的交互表现也有区别。上线前至少要保证真机双端都测一遍,别只在模拟器里自我感觉良好。
最后再分享一点我个人的体会。做小程序开发这行,有时候不是框架选得好就一定顺利,而是你是否花时间理解了底层逻辑、边界和取舍。我会在项目初期把包体积、登录态、导航栏适配这些“地基”问题在架构文档里明确写下来,一旦地基稳固,后续业务加再多也不容易乱。如果你现在正在几个框架之间犹豫不决,不妨拿出一周时间,尽量在每一个框架里都跑通一个完整的小闭环,感受一下它的编译链路、调试体验和对团队的友好度。磨刀不误砍柴工,选型这一关过了,后面做功能就是一个按部就班、验证想法的过程,这才是真正让开发变稳的那一步。