1、框架簡介
這兩天在Github上發現了xlua的作者車雄生前輩開源的一個框架—XUUI,於是下載下來學習了一下。XUUI基於xlua,又借鑒了mvvm的設計概念。xlua是目前很火的unity熱更方案,不僅支持純lua腳本熱更,也可以做 C# 代碼的bug hotfix,而mvvm框架呢,在前端開發中應用很廣,我周圍同事在做wpf開發時也用到了mvvm框架,mvvm模式在unity開發中也同樣適用,github上可以找到不少開源案例。XUUI主要有兩大核心能力:一是支持MVVM的單向、雙向綁定,二是框架應用時可以做模塊加載、模塊刷新、模塊間數據隔離、模塊間可控交互。
2、框架特點
作者在文檔中介紹了XUUI框架的特點:一是可以和任意UI庫配合,ugui,ngui,fairyGUI,你自己倒騰的UI庫都可以;二是支持把本框架作為一個mvvm驅動器,純用C#寫邏輯;三是支持“計算屬性”:“計算屬性”依賴的各屬性發生改變會觸發“計算屬性”的重計算;四是可隨時綁定View以及解綁定。
3、使用示例
下載項目到本地,可以看到作者提供了幾個使用示例。使用時要設置好綁定信息,給各個UI元素(比如Button、Text、InputField等)添加適配器,可以通過Component/XUUI菜單或者手動到XUUI\Scripts\UGUIAdapter目錄找腳本拖放到UI上,然后設置BindTo屬性即可,XUUI作者已經提供了Button、Text、Dropdown、InputField的適配器,其他ui元素比如Toggle就需要自己去編寫了,作者已經提供了接口模板,自己實現其他適配器也不難。先來看一下Helloworld示例:

這個示例中,有三個ui元素,綁定信息如下:InputField: info.name;Text : message,這是個“計算屬性”,計算時用了info.name,當info.name發生變化會觸發message重新計算,並自動更新Text;Button : click,這會綁定到一個click command上。C#代碼如下:
using UnityEngine; using XUUI; public class Helloworld : MonoBehaviour { Context context = null; void Start() { context = new Context(@" return { data = { info = { name = 'John', }, }, computed = { message = function(data) return 'Hello ' .. data.info.name .. '~!' end }, commands = { click = function(data) print(data.info.name..'哈哈') end, }, } "); context.Attach(gameObject); } void OnDestroy() { context.Dispose(); } }
如上面C#代碼所示,首先要new一個Context,參數是個lua腳本,該lua腳本返回一個table,table需要包含幾個特殊的字段: data就是ViewModle(VM);computed中引用到的VM元素,在其依賴的VM元素發生改變會自動重新計算並同步到各個綁定了它(比如上例的message)的節點;commands是類似按鈕點擊事件綁定的響應方法,隨后,調用Context的Attach方法進行綁定。
4、XUUI中幾個重要的類
(1)Context:啟動框架的方法就是new一個Context實例,並傳入lua腳本,在Context的構造函數中,會初始化好lua運行環境(即LuaEnv),並解析傳入的lua腳本,對框架各模塊進行配置,Context實現了IDisposable接口,以便對一些非托管資源進行手動的垃圾回收。
(2)DataConsumer:如果ui Adapter需要監聽VM變化,須實現DataConsumer接口(可以不顯式聲明實現,只要有DataConsumer聲明的接口即可)
(3)DataProducer:如果ui Adapter需要把數據同步回VM,須實現DataProducer接口
(4)EventEmitter:如果ui Adapter需要產生一個事件,須實現EventEmitter接口
5、XUUI框架的應用
在實際使用中,並不會像上面HelloWorld實例那么簡單,作者也提供了在實際使用時的示例,首先new一個Context,Context的構造函數傳入的是一個含modules字段以及name字段的table:
context = new Context(@" return { name = 'myapp', modules = {'module1', 'module2'}, } ");
執行上面代碼,框架會做這些事情: 加載myapp.module1,myapp.module2,加載的規則和require是一致的;為這兩個設置獨立的沙盒,各模塊即使定義了全局變量也互不影響,一定程度上減輕不同模塊開發者由於溝通不足或者筆誤引發的模塊間沖突;模塊間數據隔離:模塊也可以定義data、commands、computed,在模塊定義的commands和computed只能看到本模塊的data; 模塊間調用:通過exports字段可以導出一些函數供其它模塊調用,其它模塊可以通過“模塊名.函數名”調用
* 支持模塊刷新(reload),reload后data變動會更新UI,監聽原先commands也會自動更新到新的commands,computed會自動重新計算並更新UI。
module1代碼如下:
return { data = { name = "haha", select = 0, -- ui通過 module1.select來綁定 }, commands = { click = function(data) module2.set_select(data.select) -- 可以調用別的模塊exports的接口 data.select = data.select == 0 and 1 or 0 -- command只能看到/修改自己的數據 end, }, computed = { info = function(data) return string.format('i am %s, my select is %d', data.name, data.select) end, }, exports = { hello = function(p) -- 可以被其它module調用 print('hello, p = '.. p) end, }, }
module2代碼如下:
local data = { message = "hehe", select = 1, } return { data = data, commands = { click = function(data) module1.hello(1) data.select = data.select == 0 and 1 or 0 end, }, computed = { info = function(data) return string.format('message is %s, select is %d', data.message, data.select) end, }, exports = { set_select = function(p) data.select = p end, }, }
需要注意的是:這里的UI不像邏輯那樣划分模塊,通過“模塊名.模塊內路徑”去進行數據/響應的綁定,比如moudle1.select,module2.click等等。
6、最后
昨天才接觸到這個框架,XUUI框架下載量並不多,今天大概學習了一下,分享一下學習成果,總的來說,這個框架集成了xlua和mvvm,有很多值得借鑒的地方,很適合ui模塊的開發。博客內容有部分是從作者文檔里抄下來的,因為怕自己描述不准確。分享一下,希望這個框架能像基於ulua的SimpleFramework一樣被更多開發者使用~
項目地址:https://github.com/chexiongsheng/XUUI
如有錯誤,歡迎指正,謝謝!