文档目录
功能实现对比

引言

本技术文档围绕 DouPHP 在小程序端与 Web 端的同构能力,系统对比两者在页面渲染、数据获取、用户交互、生命周期与路由、事件通信、插件/扩展机制等方面的差异,并给出将 Web 端功能迁移到小程序的具体步骤与案例。文档以仓库中的实际代码为依据,重点覆盖小程序入口、统一 HTTP 层、全局状态管理、页面生命周期与路由等关键实现。

项目结构

  • Web 端入口与路由
    • 根入口 index.php 负责初始化前端路由、异常处理与响应发送,支持 JSON 与 HTML 两种响应模式。
    • 配置项定义应用标识、API 目录、小程序目录等常量,便于多端共用后端服务。
  • 小程序端
    • 小程序默认模板位于 miniprogram/default,包含 app.ts(应用生命周期)、services/http.ts(统一 HTTP 封装)、stores/(MobX 状态管理)、pages/(页面)。
    • 小程序通过统一的 http 层调用后端 API;通过 stores 管理登录态、购物车、通用站点信息等;通过 utils/route.ts 生成路由地址。
graph TB
A["Web 入口<br/>index.php"] --> B["前端路由与中间件<br/>front/*"]
C["小程序应用<br/>miniprogram/default/app.ts"] --> D["HTTP 客户端<br/>services/http.ts"]
C --> E["全局状态<br/>stores/*"]
D --> F["后端 API<br/>api/*"]
E --> G["页面逻辑<br/>pages/*"]

核心组件

  • Web 端入口与异常处理
    • 设置前端路由委托、解析语言前缀、启动 Init、分发路由、发送响应。
    • 对未捕获异常进行日志记录与调试页/JSON 错误响应输出。
  • 小程序应用生命周期
    • onLaunch 注册 HTTP 错误拦截器、解析推广参数、引导全局 store、计算导航栏高度、自动更新、调试开关。
    • onShow/onError/onUnhandledRejection/onPageNotFound 提供全局兜底体验。
  • 统一 HTTP 客户端
    • 信封解析(code/message/data/errors/request_id)、请求头注入、缓存与去重、SWR 刷新、调试增强、统一 ApiError。
  • 全局状态管理
    • authStore 管理登录态、权限标志、持久化与恢复,结合 commonStore/features 控制模块开关。

架构总览

小程序与 Web 端共享同一后端 API,差异主要体现在前端侧:

  • 渲染模型:Web 端由服务端渲染或前后端协作渲染;小程序端由页面 TS/WXML/WXSS 组合渲染。
  • 路由模型:Web 端基于 URL 路由;小程序端基于 pages.json 与 wx.* 导航 API。
  • 数据获取:小程序通过统一 http 层访问后端 API;Web 端可通过控制器返回视图或 JSON。
  • 状态管理:小程序使用 MobX stores;Web 端通常通过模板变量或前端框架状态。
sequenceDiagram
participant U as "用户"
participant MP as "小程序页面<br/>pages/index/index.ts"
participant APP as "应用入口<br/>app.ts"
participant HTTP as "HTTP 客户端<br/>services/http.ts"
participant API as "后端 API"
participant STORE as "全局状态<br/>stores/auth.ts"
U->>MP : 打开首页
MP->>APP : 触发 onLaunch/onShow
APP->>HTTP : 注册 onError 拦截器
MP->>STORE : restore() 恢复登录态
MP->>HTTP : GET /api/index
HTTP->>API : 发起请求
API-->>HTTP : {code,message,data,request_id}
HTTP-->>MP : data<T> 或 ApiError
MP->>MP : setData 渲染列表/分类
MP->>STORE : ensureLogin() 必要时跳转登录

详细组件分析

小程序页面生命周期与 Web 路由的对应关系

  • 小程序页面生命周期
    • onLoad:页面加载,绑定 store 字段、拉取首页数据、展示分享菜单、初始化商品列表。
    • onShow:页面显示,刷新购物车等跨页面共享状态。
    • onReady:首次渲染完成,恢复登录态并根据工作身份跳转。
    • onReachBottom:触底分页加载。
    • onUnload:解绑 store 绑定,释放资源。
  • Web 路由对应
    • Web 端通过路由匹配控制器方法,渲染视图或返回 JSON;小程序通过 pages.json 与 wx.navigateTo/switchTab 切换页面。
    • 小程序的 onShow 类似 Web 的“路由进入”回调;onUnload 类似“路由离开”。
flowchart TD
Start(["小程序页面加载"]) --> OnLoad["onLoad<br/>绑定store/拉取数据"]
OnLoad --> OnReady["onReady<br/>恢复登录态/条件跳转"]
OnReady --> Show["onShow<br/>刷新共享状态"]
Show --> Interact{"用户交互"}
Interact --> |下拉/点击| LoadMore["触底/分页加载"]
Interact --> |导航| Navigate["wx.navigateTo/switchTab"]
Navigate --> End(["页面卸载 onUnload"])
LoadMore --> End

数据获取与信封协议

  • 统一信封
    • 成功:code === 'OK',resolve(data),并在 data 上附加不可枚举元信息 message/request_id。
    • 失败:构造 ApiError,携带 code/message/errors/statusCode/request_id,供业务统一处理。
  • 缓存与去重
    • GET 请求支持内存缓存与 TTL;相同请求在飞行中复用 Promise,避免重复网络开销。
    • revalidate 选项支持 SWR 场景强制刷新。
  • 调试增强
    • 调试环境追加 request_id 尾号到 message;若服务端返回异常堆栈,弹窗提示并支持复制。
flowchart TD
Req["发起请求<br/>http.get/post"] --> CacheCheck{"命中缓存?"}
CacheCheck --> |是| ReturnCache["直接返回缓存数据"]
CacheCheck --> |否| Dedupe{"GET 去重?"}
Dedupe --> |是| Inflight["复用进行中Promise"]
Dedupe --> |否| WxReq["wx.request"]
WxReq --> Parse["解析信封<br/>code/message/data/errors"]
Parse --> Ok{"code==='OK'?"}
Ok --> |是| Success["返回data(含__message/__request_id)"]
Ok --> |否| Error["构造ApiError并reject"]
Success --> End(["业务处理"])
Error --> End

用户认证与全局状态管理

  • 登录态存储
    • api_token/user_id/loginEd 持久化至 storage;请求头自动注入 Authorization。
  • 状态恢复与校验
    • restore() 拉取 user/index 聚合登录态;UNAUTHORIZED 时清空 is_login,其它错误保留当前状态避免弱网抖动。
  • 鉴权流程
    • ensureLogin() 在需要时检查登录态,未登录则跳转登录页;app.ts 中 http.onError 统一处理 UNAUTHORIZED 登出。
sequenceDiagram
participant Page as "页面"
participant Auth as "authStore"
participant Http as "http.ts"
participant App as "app.ts"
Page->>Auth : ensureLogin()
Auth->>Auth : restore()
Auth->>Http : GET /api/user
Http-->>Auth : {dou.auth} 或 ApiError
alt 已登录
Auth-->>Page : true
else 未登录
Auth-->>Page : false
Page->>Page : 跳转登录页
end
Note over App,Http : app.ts 注册 onError -> UNAUTHORIZED 时 logout()

事件通信与跨页面/组件通信

  • 页面间通信
    • 使用 wx.navigateTo/switchTab 传递参数;通过 storage 共享如 product_category_id 等上下文。
  • 组件通信
    • 通过 props 与自定义事件(bindtap 等)传递数据;复杂场景可借助全局 store 或事件总线。
  • 全局状态管理
    • 使用 MobX stores(authStore/cartStore/commonStore)作为单一事实源,页面通过 createStoreBindings 订阅字段变化。

插件系统与扩展机制对比

  • 小程序端
    • 通过 components 目录组织可复用 UI 组件;通过 services 封装第三方能力(如上传、验证码、聊天)。
    • 通过 utils 工具函数与类型声明(types)提升可维护性。
  • Web 端
    • 通过控制器/服务/模型分层组织业务;通过 theme 模板与静态资源实现界面扩展。
    • 插件体系集中在 plugin 目录(支付、物流、社交登录等),通过路由与服务集成。

依赖关系分析

  • 小程序应用依赖
    • app.ts 依赖 http.ts(注册错误拦截器)、stores/index.ts(引导全局状态)、utils/promotion.ts(推广解析)。
    • 页面依赖 stores(MobX 绑定)、http.ts(数据请求)、utils/route.ts(路由生成)。
  • HTTP 客户端依赖
    • 依赖 types/api.d.ts(信封类型)、utils/env.ts(调试环境判断)。
  • 认证依赖
    • authStore 依赖 http.ts、route.ts、commonStore.features(模块开关)。
graph LR
APP["app.ts"] --> HTTP["http.ts"]
APP --> STORES["stores/*"]
PAGE["pages/*"] --> STORES
PAGE --> HTTP
HTTP --> TYPES["types/api.d.ts"]
HTTP --> ENV["utils/env.ts"]
AUTH["stores/auth.ts"] --> HTTP
AUTH --> ROUTE["utils/route.ts"]
AUTH --> COMMON["stores/common.ts"]

性能考量

  • 请求优化
    • GET 请求内存缓存与去重减少重复网络请求;TTL 控制缓存有效期;revalidate 支持按需刷新。
  • 渲染优化
    • 小程序页面通过 setData 局部更新;分类横滑进度条通过 nextTick 与 boundingClientRect 计算,避免频繁重绘。
  • 状态同步
    • 使用 MobX 绑定仅更新所需字段,降低 setData 压力;cartStore.refresh 在 onShow 时刷新,保证数据一致性。

故障排查指南

  • 小程序全局错误
    • app.ts 的 onError/onUnhandledRejection/onPageNotFound 提供统一错误收集与调试弹窗。
  • HTTP 层错误
    • http.ts 将网络异常、HTTP 非 2xx、业务码非 OK 统一为 ApiError;调试环境追加 request_id 并可能弹出堆栈。
  • 认证异常
    • app.ts 中 http.onError 监听 UNAUTHORIZED 并执行 logout;authStore.restore 仅在 UNAUTHORIZED 时清空登录态,避免弱网误判。

结论

DouPHP 的小程序端通过统一 HTTP 层、全局状态管理与清晰的生命周期设计,实现了与 Web 端一致的后端能力对接。小程序在渲染模型、路由模型、状态管理方面与 Web 端存在差异,但通过合理的抽象与封装,保证了功能的一致性与可维护性。对于迁移与扩展,建议优先复用现有 stores 与 http 层,遵循信封协议与错误处理规范,逐步将 Web 端功能迁移至小程序平台。

附录:迁移步骤与案例

  • 迁移步骤
    • 接口对齐:确保小程序调用后端 API 返回标准信封;必要时在后端新增或调整接口。
    • 页面重构:将 Web 端页面拆分为小程序 page,使用 createStoreBindings 绑定 store 字段。
    • 数据获取:使用 http.get/post 替代 Web 端 AJAX;启用 cache/revalidate 优化首屏与刷新。
    • UI 适配:使用小程序组件与样式;利用 utils/ui.ts 与 page_title.ts 统一交互与标题。
    • 认证流程:集成 authStore.ensureLogin 与 app.ts 的全局 UNAUTHORIZED 处理。
  • 具体案例:首页商品列表
    • 小程序首页在 onLoad 中调用 http.get(route('index')) 获取分类与站点信息,再调用 route('product') 拉取商品列表;onReachBottom 分页追加;onShow 刷新购物车。
    • Web 端对应控制器渲染视图或返回 JSON;小程序通过 setData 更新 product_list 与分页状态。
添加日期:2026-10-05