作者:閑魚技術 - 餘晏
背景
UI2CODE的目标是通過分析視覺稿得到對應的代碼,讓AI提高開發效率。然而過去靜态化頁面的産出,不能得到業務場景的需求。針對于此,我們以UI2CODE自動化開發為基底,結合Redux的消息機制,将自動化生成的次元提升到頁面的處理。
透過架構,可自動化生成頁面代碼,并且具有資料驅動展示、消息派送等動态性能力。期望在複雜的業務場景下,簡化開發的工作。并且在使用一緻化的架構下,避免未來業務代碼耦合嚴重,使代碼分工明确,容易修改維護。
進化後的UI2CODE?

開發者可以透過Intellij Plugin分析視覺稿後會生成對應的視圖代碼,以及和此頁面架構結合的能力。
在整體開發的定位上我們的野心是,提供一套可擴充的頁面消息架構,并且自動生成大部分的UI代碼。目标帶來以下的好處:
- 快速建構新應用,架構将完成大部分的事,業務開發同學隻要專注于業務代碼
- 讓開發人員的進入門檻降低,在我們落地的經驗中,後端同學隻要有基本的概念,無需花費太多經曆,可直接上手幫忙寫代碼
- 讓頁面的架構統一化,讓頁面的開發有統一的規則,也友善後續的維護
- 提供通用的元件庫,可重複利用
核心設計思路
我們在設計上主要參考于MVP、CLEAN、VIPER以及FISH_REDUX等架構。目的在實踐高聚合低耦合的前提下,分拆為視圖組裝層、視圖展示層、資料層、事件互動層。層層分離職責,不互相幹擾,又可互相通訊。
分層拆開的好處為容易維護,并且可以單元測試"業務展示"以及"業務邏輯",架構上清晰,容易有一個統一的遵循規則,達到簡單編寫重複可利用。
UI2CODE頁面架構的設計概念為,主要分為Page、Card、Reaction三大元素。在上層的Page負責組裝頁面,制定頁面的風格。Card則為可重複利用的視圖展示元素。Reaction則為事件反應的監聽。
在整個頁面架構上,可以透過UI2CODE Plugin分析自動化生成業務UI,産生出Page、Card、Card(DataModel)。僅需修改Card上額外的業務展示,以及撰寫Reaction中的業務邏輯。
具體實作架構
在介紹架構元件前,先了解UI2CODE的基本組成頁面目錄如下:
以Page為機關,頁面本體demo_page為其他頁面路由調用的起點,透過設定Config.dart決定内部含的卡片清單以及事件處理清單,組合出Card以及Reaction的關聯。
其詳細的架構關系如下:
Page
Page為架構基礎的機關,為單一頁面,負責決定視圖的組裝以及頁面的樣式(Template)。
在Page之内可包含若幹的Card以及Reaction,分别為視圖的展示以及視圖的事件處理。可以很清晰地将業務場景做分割成小子產品,不互相影響。
Abstract class PageStatelessWidget extends StatelessWidget
implements Lifecycle
- 可由UI2CODE Plugin自動化産生
- 架構統一分發管理頁面生命周期Lifecycle
- 透過設定Template指定頁面要呈現的樣版,或者修改如背景等屬性
- 透過設定Config指定這個頁面含有的Cards和Reactions
- 透過設定PageState可添加額外的資料
Page Template
Template樣闆為頁面的抽象化,在整體頁面上分為多個樣闆可選擇。并且支援設定AppBar(非必選)、Header(非必選)、Body、Stack(非必選)等子樣闆。
樣闆可比喻為頁面的容器,目前支援以下樣闆,并且可擴充:
- PageTemplate,通用頁面容器,并支援NestedScrollView的Silver Header滾動(若需要)
- PageBottomNavigatorTemplate,含有底部導航的容器,如首頁
- PageSwitchTabTemplate,含有分頁Tab功能的容器
各個子樣闆也有相對應的Template可選擇,如在Body内的樣闆功能為決定内部Cards排列的方式。舉例來說,BodyListViewTemplate則是清單展示。
使用Template最大的好處為減少開發工作,可直接使用封裝後的接口。并且頁面内的所有樣闆将共用消息機制,可以互相傳遞消息,如Body内部的卡片很容易發送消息給AppBar等。這是架構上的有力之處。
Page Config
Config決定頁面的組裝,包含了元件有哪些,以及事件處理反射的類綁定。
Extends PageConfig
- 透過設定cards注冊這個頁面所載入的卡片
- 透過設定actions注冊這個頁面所響應的類,支援卡片事件以及頁面事件
- 支援額外設定AppBar、Header、Stack等元件(非必須)
如何綁定,舉例來說:
void actionConfig(List< List < Function>> actions) {
//卡片Card8575, 響應事件的類為Card8575Reaction
actions.add(< Function>[() => Card8575, () => new Card8575Reaction()]);
}
Card
Card代表基本的視圖展示,業務UI,其中包含了View widget以及DataModel資料。架構内會将兩者Data binding起來,由資料來驅動最終展示的呈現。達到如MVP中View和ViewModel的隔離效果。
Abstract class BaseCard<T extends DataModel> extends StatefulWidget
- 可由UI2CODE Plugin分析視覺稿産生
- 透過BaseCard<T extends DataModel>的标準化,指定資料DataModel綁定
- Card可以發出事件,不直接操控資料,而讓接收者響應
Reaction
Reaction代表著事件發生的響應,可以選擇是否處理事件。若選擇處理,可同步或異步修改對應的資料DataModel。
Abstract class CardReaction<S, D extends DataModel>
- init()為初始化事件,自動發出,可進行一些初始動作
- RegisterReactions()注冊感興趣的事件handler
- 可于handler上加上aysnc,指定為異步處理
- Reaction内依據事件修改DataModel,隻要關注事件改變後的資料,本身不持有資料,視圖将會自動重新整理
舉例來說:
//如發送rAssignPrice的事件
doAction(Card6736Event.rAssignPrice);
//接收rAssignPrice的事件, 并對資料做處理
@override
Map
Card6736Event.rAssignPrice: (PageItemBean state, CardAction action, Card6736DataModel data) {
//修改資料欄位
data.userName = "123";
},
};
結合Redux
在頁面架構的背後,我們采用了Redux來進行封裝。
Redux是一套的資料管理的架構。所有的資料的儲存于Store的State内。當事件發生時,會發生不同的Action,根據事件響應不同的Reducer去改變State。若經過響應後State有所變化,則綁定的視圖會視需要做對應的重新整理。
詳細Redux:可以參考
https://github.com/reduxjs/redux我們應用了Redux中等State、Action、Reducer、Store、Middleware的概念,将頁面賦予生命狀态,而頁面内的元件間可支援消息機制。Redux對剛入門的同學還是有一定的門檻所在,但在本架構的封裝下,基本上感覺不到Redux的存在。
消息機制
在UI2CODE消息架構下,頁面内的各個元件以及容器都可以彈性的進行消息傳遞。可以由Page、Card、Reaction等處任意的發送消息,達到(自身、兄弟間、子對父、父對子)的通知。
各種消息傳遞的路徑說明如下:
- 自身:Card發出的消息将由自身定義的Reaction處理
UI2CODE再進化!結合Redux的架構更新! - 兄弟間:Reaction内可選擇轉發,可以指定轉發的對象為另外一張Card
UI2CODE再進化!結合Redux的架構更新! - 父對子:可于Page内指定發送的Card
UI2CODE再進化!結合Redux的架構更新! - 子對父: 若發出的消息在Card内無人處理,則會冒泡到Page層級處理
總結進化的UI2CODE
- 架構簡單,隻需了解架構基本的元素,不需要會Redux就可以達到資料管理的效果。目前閑魚内部的新應用落地,所有的頁面均透過架構的機制來達到消息傳遞。而其中1/3頁面UI為透過自動化生成,減少了約一半的整體開發時間。
- 因為元件的分層解耦,維護時可以清晰看到頁面的組成及覆用代碼。并且在修改元件時,不影響到其他元件的作用。
- 事件可以在頁面架構下自由的傳遞,達到高聚合的效果,并且響應支援同步和異步的流程。開發者隻需要關心資料處理,視圖的重新整理将會由架構處理。
未來展望
透過整合UI2CODE Plugin,使用插件可透過AI自動分析産生Page、Config、Card等。開發者可以在自動化的基礎上再進行修改,大大減少從無到有的開發時間。我們期望開發者隻需要專注的修改業務展示以及業務邏輯,不需要對頁面設定做過多的處理。
透過與業務上的合作,我們獲得了很多實際上的想法,以及對于不同業務場景的适應。在這些經驗上不斷地優化架構,讓架構更解耦,支援能力更多。未來我們希望是不隻閑魚内部的應用,也擴充給更多的應用!