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菜单或者手动到XUUIScriptsUGUIAdapter目录找脚本拖放到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
如有错误,欢迎指正,谢谢!