在研究大模型UI架構(gòu)的時候,我意識到一個優(yōu)秀的架構(gòu)能夠讓使用者隨心所欲而不逾矩:
千變由意起,斗轉(zhuǎn)又星移
萬相唯心生,毫末不逾矩
紛紛擾擾皆塵事,不入我心自無敵。
格物致知,深入研究技術亦可領悟大道。
好了,言歸正傳,開啟正文~
一、大模型 UI 為什么“天然復雜”
在傳統(tǒng)頁面中,UI 通常是“確定的”:
- 一個列表
- 一個詳情頁
- 固定布局
但在大模型場景中:
?? UI 是不確定的
用戶一句話:
- “北京天氣”
- “播放周杰倫”
- “幫我規(guī)劃路線”
云端可能返回:
- 天氣卡片 ??
- 音樂卡片 ??
- 導航卡片 ??
- 多卡片組合
??UI不再由客戶端決定,而是由用戶意圖(實際上是由意圖得到的數(shù)據(jù))“驅(qū)動”。
二、核心思想
Intent(意圖) → Reducer(轉(zhuǎn)化器) → State(狀態(tài)) → UI
↓
Effect(副作用)
關鍵步驟
把UI變化的所有原因收斂為一個完整的State。
建立State到UI的映射:UI=f(State)
建立起用戶意圖統(tǒng)一管理Intent—所有變更只能走 Intent
構(gòu)建純函數(shù)類Reducer 負責State演進,newState=g(intent+oldState)
副作用統(tǒng)一管理
視圖層只負責發(fā)送意圖+渲染
三、UI架構(gòu)設計步驟
需要先明確一個前提:
MVP、MVVM、以及我們討論架構(gòu),核心都是“頁面級 UI 架構(gòu)”。
它們解決的問題是:一個頁面(或一個 UI 場景)內(nèi)部,狀態(tài)如何管理、業(yè)務如何解耦、界面如何穩(wěn)定演化,而不是整個 App 的全局架構(gòu)。
第一步:UI State建模
UI State 建模 = 從“UI變化”反推“變化原因”,再從原因中篩選“最小控制變量集合”
詳細展開可以參考《UI State如何建模?》
第二步:按觸發(fā)來源分類枚舉 Intent
這里我給出大概分類
1、用戶操作
對著設計稿 / 原型,指著每一個可交互元素問:如何操作它呢?
- 點擊按鈕
- 點擊列表項
- 輸入文字
- 切換開關
- 滑動加載更多
- 下拉刷新
- 長按
...
注意:一個控件只寫一個 Intent,不要按結(jié)果寫。
比如:點擊登錄按鈕 → ClickLogin,
不要寫成 LoginSuccess / LoginFail(那是 State)。
2、頁面生命周期
凡是頁面自己 “出生到死亡” 會變的,我們都要思考哪些會引發(fā)UI變化,從中篩選會引發(fā)UI變化的意圖。
- 頁面創(chuàng)建
- 頁面可見
- 頁面不可見
- 頁面重建(配置變更)
- 狀態(tài)恢復
- 權限返回結(jié)果
3、異步結(jié)果(后端 / 數(shù)據(jù)層)
- 接口請求成功
- 接口請求失敗
- 列表加載更多成功 / 失敗
- 本地數(shù)據(jù)庫讀取完成
- 定時任務回調(diào)
- 上傳 / 下載進度
當一種異步結(jié)果會引發(fā)的UI改變,那就需要加入Intent集合。
4、外部事件(系統(tǒng) / 全局)
- 網(wǎng)絡斷開 / 恢復
- 登錄態(tài)過期
- 推送點擊
- 深鏈跳轉(zhuǎn)
- 語言 / 主題切換
- 應用切前臺 / 后臺
- 其他頁面返回結(jié)果
凡是頁面外的東西能影響頁面內(nèi),都要加 Intent。
5、UI 內(nèi)部事件
不是用戶直接點某個按鈕,而是因UI變化產(chǎn)生的事件也需要加入Intent集合,例如
- 滾動到底 / 滾動到頂
- 鍵盤彈出 / 收起
- 視圖獲得 / 失去焦點
- 卡片展開 / 收起
- 面板展開 / 折疊
- 頁面滾動狀態(tài)變化
- 列表自動加載更多
把所有Intent進行一個統(tǒng)一管理,作為唯一入口,比如:
// sealed class限制它的子類只能寫在它內(nèi)部
sealed class XxxIntent {
// 定義一個繼承密封類XxxIntent的單例
object OnRefresh : XxxIntent
object OnLoadMore : XxxIntent
// 定義一個繼承密封類XxxIntent的數(shù)據(jù)類OnInputText
data class OnInputText(val text: String) : XxxIntent
// 子項級
data class OnItemClick(val id: String) : XxxIntent
data class OnItemSwap(val id1: String, val id2: String) : XxxIntent
data class OnItemDisturbed(val id: String) : XxxIntent
// 系統(tǒng)級
data class OnError(val msg: String) : XxxIntent
object OnDialogDismiss : XxxIntent
}
注意:進行交叉驗證
拿到前文定義的 State,逐個問:
這個 State 是被誰改變的?
如果找不到對應的 Intent
說明漏了intent定義
也可以再次拿定義的Intent,逐個問:
它會引發(fā)什么UI變化,
這種UI變化有沒有對應的State
如果沒有說明漏了State定義
第三步:寫狀態(tài)轉(zhuǎn)換器 Reducer
ruduce 簡化、歸一的意思
reduce函數(shù)就是把 action+state 兩個變量 轉(zhuǎn)換歸一為一個new state
它是一個純函數(shù):
old State + Intent → new State
規(guī)則:
- 不修改原對象
- 只用 copy
- 不寫副作用
- 所有分支清晰
模板:
fun reduce(
oldState: XxxUiState,
intent: XxxIntent
): XxxUiState {
return when (intent) {
is XxxIntent.OnRefresh -> {
oldState.copy(isRefreshing = true)
}
is XxxIntent.OnItemClick -> {
oldState.copy(selectedId = intent.id)
}
is XxxIntent.OnItemSwap -> {
val newItems = oldState.items.swap(intent.id1, intent.id2)
oldState.copy(items = newItems)
}
is XxxIntent.OnError -> {
oldState.copy(
pageState = PageState.Error(intent.msg),
isRefreshing = false
)
}
// ... 所有 Intent 全覆蓋
}
}
第四步:副作用單獨抽離(Effect)
副作用指除了更新 State ,額外干的事。
凡是不會直接返回新 State,但會觸發(fā)外部 IO、改變外部環(huán)境、或產(chǎn)生一次性效果的行為,統(tǒng)稱為副作用(Side Effect)
- State:可重現(xiàn)、可恢復、可持久化 → 不是副作用
- 副作用:一次性、不可重放、執(zhí)行后就消失、不存進 State → 是副作用
比如:
-
顯示“加載失敗”文字→ 屬于 State,不是副作用 -
彈出一個“加載失敗”的 Toast→ 副作用
常見的副作用有:
- 跳轉(zhuǎn)頁面
- Toast
- 埋點
- 震動
- 請求網(wǎng)絡
- 數(shù)據(jù)庫操作
sealed class XxxEffect {
data class ShowToast(val msg: String) : XxxEffect
data class NavToDetail(val id: String) : XxxEffect
data class requestData(val requestParam: String) : XxxEffect
}
完整閉環(huán)鏈路
View 發(fā)送 Intent
↓
ViewModel → 調(diào)用 Reducer → Reducer 生成新 State → View監(jiān)聽到State變化渲染UI
↓
EffectDispatcher副作用調(diào)度器
↓
XxxEffect副作用處理器執(zhí)行副作用(比如執(zhí)行請求網(wǎng)絡)
網(wǎng)絡請求回調(diào)產(chǎn)生新的“數(shù)據(jù)請求成功intent” 給viewModel
小結(jié)
Reducer相當于Ui State狀態(tài)機,EffectDispatcher相當于Effect 狀態(tài)機,它們是兩套是解耦的流
Reducer不關心網(wǎng)絡怎么請求
EffectDispatcher不關心UI怎么渲染
它們一個管理“內(nèi)部世界“,一個管理“外部世界”,
整個復雜 UI 系統(tǒng)本質(zhì)就是:
??“狀態(tài)機 + 副作用機”的協(xié)同系統(tǒng)。
四、UI架構(gòu)如何跟大模型結(jié)合
當UI 被抽象為:
- Intent(意圖)
- State(世界狀態(tài))
- Reducer(演化規(guī)則)
- Effect(外界交互)
UI 系統(tǒng)便不再只是“代碼”,而開始成為:
?? 一個可被 AI 理解、推理、生成、演化的狀態(tài)世界。
因為對于大模型而言,最難理解的從來不是語法,而是隱藏在代碼里的狀態(tài)變化邏輯。
傳統(tǒng) UI 開發(fā)中:
- 狀態(tài)散落在各個 View
- 邏輯分散在回調(diào)與生命周期
- UI 與業(yè)務強耦合
- 大量 if else 隱式驅(qū)動界面變化
這會讓整個系統(tǒng)變成:
“不可描述系統(tǒng)”
而當系統(tǒng)被明確拆解為:
什么導致變化(Intent)
↓
狀態(tài)如何演化(Reducer)
↓
當前世界是什么(State)
↓
如何與外界交互(Effect)
整個 UI 系統(tǒng)就第一次擁有了:可推理性
AI 可以理解并且生成:
- 當前頁面處于什么狀態(tài)
- 哪些事件會導致狀態(tài)遷移
- 哪些行為屬于副作用
- 哪些 UI 是狀態(tài)的結(jié)果
于是大模型可以參與:
- UI 狀態(tài)設計
- 交互流推演
- 狀態(tài)機生成
- 副作用編排
- 頁面自動演化
UI 開發(fā)也會逐漸從“堆砌頁面邏輯”轉(zhuǎn)向→設計“可推理的狀態(tài)系統(tǒng)”。