初探復雜UI架構(gòu)設計:大模型UI的狀態(tài)驅(qū)動實踐

在研究大模型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)”。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容