🚀 多端架构最难的不是 UI,而是这 3 个致命陷阱
在前两篇中,我们完成了两件看起来“很重要”的事情:
- 第 2 篇:架构设计(UI / modules / services)
- 第 3 篇:Monorepo 工程落地
很多人会在这个阶段产生一个错觉:
pnpm install 能跑通 = 架构已经完成
但现实是:
这只是“代码能运行”,不是“架构成立”
👉 物理上的代码共享(Code Sharing),并不等于逻辑上的跨端通用。
真正的架构深水区,才刚刚开始。
🧠 一、问题的本质:我们到底在共享什么?
很多人做多端,第一反应是:
UI 怎么统一?
但在真实项目中你会发现:
UI 反而是最容易解决的
真正的崩溃场景是:
- 在小程序里运行包含
window.location的逻辑 ❌ - 在 Web 端处理不了小程序登录态 ❌
👉 本质问题是:
业务逻辑无法脱离平台独立运行
👉 所以跨端的核心不是:
“怎么写兼容代码”
而是:
“如何让核心逻辑感知不到平台的存在”
🔥 二、陷阱 1:Request 层的“名义复用”
❗ 问题现象
在 Web 中:
axios.get('/api/user')
在小程序中:
wx.request({ url: '/api/user' })
如果你在 services 层这样写:
// ❌ 无法跨端复用
export const getUserInfo = () => axios.get('/api/user')
👉 结果是:
业务逻辑被平台 API “污染”
🔥 本质问题
请求能力没有被抽象,只是被“换了个写法”
✅ 解决方案:Adapter Pattern(适配器模式)
不仅仅是封装 URL,而是:
统一请求协议 + 抹平响应结构 + 收敛拦截逻辑
📦 推荐结构
packages/request/
├── core/
├── adapters/
│ ├── web.ts
│ ├── mini.ts
💻 核心实现
// core/types.ts
export interface UnifiedResponse<T> {
code: number
data: T
msg: string
}
// core/request.ts
export async function request<T>(options): Promise<UnifiedResponse<T>> {
const config = preRequest(options)
const rawResponse = await adapter(config)
return normalize(rawResponse)
}
🔥 关键结论
request 层的本质,是把“环境差异”收敛到一个边界内
👉 最终效果:
services 层完全变成“环境无关”的纯函数
⚠️ 三、陷阱 2:Modules 的“重力坍塌”
❗ 常见现象
随着业务复杂:
modules 变成 utils 大杂烩
或者:
组件中直接写业务逻辑
👉 最终:
复用失败 + 代码爆炸
🔥 本质问题
缺乏清晰的“领域边界”
❗ 一个典型错误
// ❌ 不可跨端
showToast('登录成功')
👉 为什么错?
因为 UI 行为被写进了业务逻辑
✅ 正确做法:Environment Agnostic(环境无关)
modules 必须是纯粹的领域层(Domain Layer)
📦 推荐结构
modules/user/
├── model.ts
├── service.ts
├── hooks.ts
├── types.ts
💻 示例
export function useUserDomain() {
const [user, setUser] = useState(null)
const login = async (credentials) => {
const data = await authService.login(credentials)
setUser(data)
}
return { user, login }
}
🔥 关键认知
modules 不是工具库,而是业务的“大脑”
大脑不应该知道,它是在控制网页,还是小程序
❗ 更进一步的判断标准
如果你的 modules 可以被随便 copy 到另一个项目直接使用,
那它大概率只是“工具库”,而不是“领域层”
🎨 四、陷阱 3:Design System 的“皮同肉不同”
❗ 常见误区
Design System = 组件库
👉 于是开始:
疯狂封装 Button / Input
👉 结果:
不同端体验依然割裂
🔥 本质问题
只有组件实现,没有设计规则
✅ 正确做法:三层设计系统
📦 结构
design-system/
├── tokens
├── foundations
├── patterns ⭐
🔥 最关键:patterns
例如:
列表页必须包含:
- 筛选区
- 数据流
- 空状态
- 错误态
👉 不同端实现:
| 端 | 实现方式 |
|---|---|
| Web | React |
| 小程序 | WXML |
👉 但核心是:
行为一致,而不是外观一致
🔥 核心结论
Design System 的目标,不是“长得一样”,而是“行为一致”
👉 实现方式:
共享逻辑(Headless) + 各端 UI 自实现
🧠 五、总结:抽象是唯一的出路
看似是 3 个问题,本质只有一个:
抽象能力不足
🔁 抽象分层
| 层级 | 解决问题 | 手段 | 抽象 |
|---|---|---|---|
| 基础层 | 环境差异 | request adapter | IO 抽象 |
| 业务层 | 逻辑复用 | modules | 业务抽象 |
| 体验层 | 交互一致 | design patterns | 体验抽象 |
🔥 更深一层理解
抽象的本质,不是让代码更通用
而是让变化被限制在可控范围内
👉 所以:
跨端的核心,不是技术,而是抽象能力
🚀 结语
很多人做多端,会陷入一个误区:
不断写 if (isMiniProgram)
但这本质上是:
在用补丁对抗复杂度
👉 真正的架构应该是:
让平台差异被“隔离”,而不是被“处理”
🔮 下一篇预告(完结篇)
多端架构的终局:前端平台化
👉 我会从更高视角讲清楚:
当逻辑彻底解耦后,前端如何进化为“平台能力”
🔥 这一篇,将决定你能否完成从“工程实现”到“架构思维”的跃迁。
💬 聊聊你的看法
你在多端开发中,遇到过最离谱的“平台差异坑”是什么?
欢迎在评论区分享,一起避坑 👇
#前端架构 #跨端开发 #Monorepo #React