小程序看起来轻量,但一旦业务变复杂,页面数量、接口调用、状态共享会迅速膨胀。如果没有清晰的架构约束,代码会在两三个月内变成一团难以维护的泥。这篇文章从分层、状态、通信、构建四个角度,讲一套可以直接落地的小程序开发架构。

一、先分层,再谈其他
小程序官方没有强制架构,但推荐的最小分层是三层:页面层、逻辑层、数据层。我的做法是在 src 下按职责划分目录,而不是按页面类型平铺。
src/
pages/ # 页面,只做渲染与事件转发
components/ # 通用组件
services/ # 业务逻辑,纯函数优先
api/ # 接口定义与请求封装
store/ # 全局状态
utils/ # 工具函数
config/ # 环境配置
关键约束只有一条:页面层不允许直接调用 wx.request。所有数据获取都走 api 层,业务组装走 services 层。这样页面代码只剩下 setData 和事件绑定,测试与复用都变得简单。
二、请求层要做的事比想象中多
很多项目的 api 层只是把 URL 拼一拼,结果登录态、错误提示、loading 状态散落在各个页面。建议把请求封装成一个带拦截能力的实例:
// api/request.js
const BASE_URL = process.env.API_BASE
function request(options) {
return new Promise((resolve, reject) => {
wx.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'content-type': 'application/json',
Authorization: wx.getStorageSync('token') || ''
},
success(res) {
if (res.statusCode === 401) {
// 统一跳登录,避免每个页面重复判断
wx.navigateTo({ url: '/pages/login/index' })
return reject(new Error('UNAUTHORIZED'))
}
if (res.data.code !== 0) {
wx.showToast({ title: res.data.msg, icon: 'none' })
return reject(res.data)
}
resolve(res.data.data)
},
fail: reject
})
})
}
这一层解决三件事:统一鉴权、统一错误提示、统一返回值结构。页面里只需要 const list = await api.getList(),不必关心 token 和错误码。
三、状态管理别急着上库
小程序的全局状态其实很少:用户信息、登录态、少量配置。完全可以用一个极简的发布订阅实现,没必要引入重量级方案。
// store/index.js
const state = { userInfo: null, token: '' }
const listeners = new Set()
export function setState(patch) {
Object.assign(state, patch)
listeners.forEach(fn => fn(state))
}
export function subscribe(fn) {
listeners.add(fn)
return () => listeners.delete(fn)
}
export function getState() {
return state
}
页面在 onLoad 中订阅,在 onUnload 中取消订阅。注意一定要取消,否则页面销毁后回调仍在执行,会触发对已卸载页面的 setData,控制台出现警告甚至内存泄漏。
四、组件通信的边界
父子组件用 properties 与 triggerEvent 就够了。真正容易出问题的是跨层级通信,比如弹窗、登录框这类全局组件。推荐两种方式:
- 简单场景:用上面的事件总线,全局组件订阅特定事件;
- 复杂场景:把全局组件挂在根页面,通过
selectComponent获取实例调用方法。
无论哪种,都要明确“谁拥有状态”。我的习惯是:全局组件自己管理显隐,业务方只发指令,避免多处同时修改同一个布尔值导致状态错乱。
五、构建与分包
主包体积是硬约束。架构上要提前规划分包:把低频页面(活动页、设置页、帮助中心)放进分包,公共组件和工具放主包。配置示例:
{
"pages": ["pages/index/index"],
"subPackages": [
{
"root": "packageActivity",
"pages": ["pages/detail/index"]
}
],
"preloadRule": {
"pages/index/index": {
"network": "all",
"packages": ["packageActivity"]
}
}
}
配合 preloadRule 在首页空闲时预下载分包,用户点进去几乎无感。这一步对留存的影响比很多 UI 优化都大。
六、性能上的几个硬指标
setData单次数据量控制在几十 KB 以内,列表分页加载,不要一次性塞几百条;- 长列表使用
recycle-view或手动虚拟列表,避免节点数爆炸; - 图片走 CDN 并带尺寸参数,首屏图片开启
lazy-load; - 避免在
onPageScroll里频繁setData,用节流或改由 CSS 实现动效。
如果项目用云开发,数据库查询同样要注意索引和分页,limit 默认 20 条,聚合查询的 lookup 在数据量大时开销明显,能冗余字段就不要频繁联表。
七、写在最后
架构不是越复杂越好,而是让“新增一个页面”这件事有固定路径可走。分层清晰、请求统一、状态可控、分包合理,这四点做到,小程序的维护成本会下降一个量级。真正的架构决策,往往发生在项目第二个月而不是第一天,所以留好扩展点比一次设计到位更重要。

评论 (0)
还没有评论,快来抢沙发吧~