uni-app x下自研架构的工程化突围

本文是 unix-architecture(v1.1.0)的设计与实践总结。技术栈:uni-app x(VDOM 模式)· UTS 语言 · Vue3 组合式 API,底层由自研插件系(ux-router / ux-pinia / mx-request / ux-storage / ux-utils / m-ui-x)承载。

一、为什么需要「重新发明」分层

如果你写过标准的 Vue3 工程,大概率熟悉这套形态:vue-router 单路由表、pinia 挂载即用、axios 拦截器全局生效、vite 约定式路由。一切顺理成章。

但 uni-app x 不是标准 Vue3。它的页面是编译期静态模型 ------同一份 .uvue 代码要编译到 JavaScript(Web / 小程序)和 Kotlin(Android)、Swift(iOS),没有动态路由注册,没有全局 layout,甚至 computed 在某些插件场景下都会失效。标准 Vue3 的「单路由表、单插件协议」在这里天然不成立。

unix-architecture 想验证的问题是:在 uni-app x 的底座约束下,能不能构建一套完整自洽的工程化分层? api、store、router、utils、pages 各司其职,类型契约清晰,装配点唯一。

答案是能,但每一层都要重新想一遍。

二、分层总览:依赖单向,职责单一

整个架构是一条严格单向的调用链:

复制代码
页面(.uvue)
  → composables/useXxx        横切 UI 状态(loading 等)
  → stores/modules/*          业务状态与动作
  → api/<domain>              请求转发 + 路径收敛 + DTO 还原
  → utils/request             拦截器:token 注入 + 统一错误提示
  → mx-request → uni.request  底层网络

几条铁律贯穿始终:

  • 页面不接触裸 JSON------页面永远消费还原后的 DTO,网络细节被彻底隔离;
  • api 层薄如纸------只做请求转发、路径收敛和 DTO 还原,禁止写业务逻辑;
  • store 不引用组件,组件不反向依赖 store 之外的任何层;
  • 装配点唯一 ------main.uts 保持极薄,只有三行:app.use(router)、initRequest()、setupPermission(),任何全局副作用都不允许在别处发生。

支撑各层的插件系由同一个工作室自研,彼此语义咬合:ux-router 提供命名路由与守卫链,ux-pinia 提供免挂载的 class 式状态管理,mx-request 提供双拦截器网络层,ux-storage 负责异步持久化,ux-utils 提供全端自绘的 toast/modal。

三、三个「标准姿势失效」的地方

架构不是照搬出来的,而是被约束逼出来的。以下是三个最典型的「标准 Vue3 直觉在这里不工作」的例子。

1. Pinia 的三种装配范式并存

标准 pinia 是 app.use(pinia) 一行搞定。但在 uni-app x 的原生端,实测 app.use(pinia) 不可靠。于是本架构中三种装配范式并存:

  • ux-router :实现了 Vue install 协议,走 app.use(router);
  • ux-pinia:免挂载设计,模块级注册表即全局单例,无需注册;
  • mx-request :纯函数库,全局默认实例,显式调用 initRequest()。

「三种范式并存」听起来不够统一,但它是底座约束下的诚实设计------强求形式统一只会埋进更深的坑。

2. token 的同步读写体验

js-cookie 是同步的,但 uni-app x 的 storage API 是异步的。鉴权守卫却是「同步心智」:beforeEach 里读 token、判断、重定向,中间塞一个 await 就要全链路重想。

解法是双层缓存 :token 读写永远走模块级内存缓存(同步体验),后台由 ux-storage 异步落盘;冷启动时守卫首行 await ensureAuthReady()(Promise 复用,只读一次持久层)先把内存缓存恢复好。于是:

复制代码
登录 → setToken(内存即时生效 + 异步落盘)
请求 → 拦截器注入 Authorization: Bearer
冷启动 → ensureAuthReady() 先行恢复内存缓存,守卫无竞态

3. 静态模型下的路由表与守卫

uni-app x 没有动态路由。本项目用 vite 插件从 pages/ 目录编译期生成 routes.gen.uts(禁止手改),配合页面内 defineUniPage({ name, title }) 宏声明元数据------新增页面 = 建文件 + 注册 + 声明宏,路由表自动维护。

守卫链则补齐了 vue-router 的核心体验:白名单 + token 检查 + getInfo + 重定向,冷启动直达页面(H5 deeplink)时 router.isReady().then(() => router.guardRoute()) 补跑守卫链。

四、UTS 实战:那些只有跑起来才知道的坑

UTS 是名义类型系统,编译到 Kotlin 后很多 JS 直觉会翻车。以下是几条用真机打包失败换来的红线:

  • DTO 还原必须逐调用点内联 JSON.parse<T>(JSON.stringify(res.data))。提取一个「公共泛型包装函数」看似优雅,但二次泛型传递在 Kotlin 端触发泛型擦除,直接编译失败。架构宁可接受重复代码,也换取编译确定性。
  • 类型转发链有布局约束 :app-android 的发行拷贝不追踪 export type ... from------仅被类型转发引用的子目录文件根本不进发行树,分析器直接报 index not found。因此域类型收敛在 <域>/types.uts 单文件内定义,禁止 types/ 子目录转发链。
  • 没有 undefined ,一切可空用 null | T;条件语句必须是显式布尔表达式,truthy/falsy 写法直接报错。
  • 重定向对象禁止裸字面量 :守卫重定向必须用导出的 RawLocation 类型构造------UTSJSONObject 在 Kotlin 名义类型下 cast 会崩溃。
  • 守卫内禁 computed 字段(插件内 effectScope 跨页面失效),派生值一律用 class getter。

这些约束最终全部沉淀进了项目的架构规则文档,成为 AI 辅助开发的强制约束层------违反即视为错误实现,无论写法在 TS 里多么「惯用」。

五、类型跟着消费关系走

类型组织只有一条判据:类型跟着消费关系走。

类型性质 位置
跨层共享(api → store → 页面/守卫) <域>/types.uts 单文件定义
store 出口形状 class 声明 + 域 types.uts 转发
单文件模块私有契约 就近定义(如 config 的 AppSettings)
第三方库类型 import type 导入,绝不复制

这套规则消灭了两类常见顽疾:类型定义散落各处导致「改一个字段全局搜」,以及复制第三方类型导致「库升级后类型漂移」。

六、把「AI 怎么写代码」也当成架构的一部分

这是本项目最有意思的一个设计:仓库里有一个 .trae/ 目录,把「AI 助手如何在此架构下工作」沉淀为可执行的规则与技能,与代码同库演进。

  • architecture.md:机器强制约束层------分层调用链、插件导入路径唯一事实源、UTS 红线、标准落码顺序(类型 → api → store → 页面,缺一不可);
  • core-protocol.md:协作协议------辩证执行、DoD 以运行时表现为准(编译通过不算完成,日志验证才算)、熔断机制;
  • workflow.md:文档驱动工作流------需求分析、伪代码设计、疑问清单全部落盘跨会话持久,对话只做通知与执行;
  • skills/:新功能开发按固定流程落码的技能封装。

原理层文档回答「为什么这样设计」,规则层回答「AI 必须怎么写」,二者同源同步演进。实践下来,这让 AI 生成的代码从「看起来对」变成「架构上对」------新功能天然落在正确的层,类型天然进正确的文件。

七、写在最后

unix-architecture 目前已发布 v1.1.0,Web 端全链路验证通过(冷启动守卫、登录闭环、DTO 链路),Android 已知风险点均已规避并经云打包验证。

它的价值不在代码量------模板本身很小------而在于把 uni-app x 这类「非标准运行时」上的工程化经验显式化了:每一处与标准 Vue3 的偏离,都对应一条被实测验证过的约束和一条明确的规则。

如果你也在多端编译框架上做工程化,希望这份踩坑记录能帮你少烧几次打包机。


相关推荐
速易达网络2 小时前
ECharts 图表制作完全指南:从零到可视化实战
前端·javascript·echarts
这个DBA有点耶2 小时前
连拿六年第一后宣布出海:国产数据库凭什么敢撕掉“平替”的标签?
数据库·人工智能·架构
IT_陈寒2 小时前
SpringBoot多环境配置把我坑惨了,这个踩坑经验你最好看看
前端·人工智能·后端
徐小夕2 小时前
深度剖析 30k star 的编码 Agent DeepSeek-Reasonix:长会话 Token 成本直降 80%
前端·vue.js·github
轻松西西莱尔2 小时前
谷歌+斯坦福+CMU 等五家 203 页综述,我做了个HTML把主动式AI的边界跑给你看
前端·html·agent
高晶2 小时前
SPMI 控制器 RTL 设计与验证新手入门
设计模式·程序员·架构
吴凡wudianmei2 小时前
从 0 搭一个 AI Agent(四):SSE 跑通之后,三处“契约错配”与它们的修复边界
前端·后端·aigc
不可能片场2 小时前
Electron 发布 别拿 load 事件当页面就绪
前端·electron
paopaokaka_luck2 小时前
灾害应急物资管理系统(AI应急助手、Echarts可视化数据看板、捐赠入库联动、 物资全流程管理、库存变动追溯、灾情资讯与互动反馈、物资发放公示)
java·前端·javascript·spring boot·mysql·tomcat·echarts