首页 / 技术文章地图 / 正文

【框架设计】MVC 轻量实现:界面与数据分离的 Lua 方案

发布:2026-09-20 07:58 | 作者:996 技术组 | 1 阅读
完整课程入口:996 全套课程体系Lua 学习路径幂尔框架 mirs.cn

界面代码为什么越写越乱

新手写界面:按钮回调里直接改数值、刷新文本、发包,一个函数干三件事。等第二个入口也要改同一个数值时,要么复制粘贴,要么到处打补丁。MVC 的价值就在这里:Model 管数据,View 管显示,Controller 管转换——数据变了界面自动跟着变,界面永远只是数据的镜像。

Lua 里的极简 MVVM

Lua 动态特性让"数据绑定"可以做得非常轻。核心思路:Model 的每个字段带观察者列表,写入时通知所有订阅的 View 刷新。

lua
local function bind(model, key, onChange)
    local raw = model[key]          -- 闭包持有真实值
    return setmetatable({}, {
        __index = function() return raw end,
        __newindex = function(_, _, v)
            raw = v
            onChange(v)             -- 数据一变,界面自动刷新
        end,
    })
end

-- View 订阅
gold = bind(state, "gold", function(v)
    goldLabel:setText("金币:" .. v)
end)

-- 任何地方只改数据,界面自动更新
gold = 8800

配合上一期的 EventBus 使用效果更佳:Controller 监听业务事件改 Model,Model 变更再广播刷新事件,三层各司其职。

三条落地规则

第一,View 禁止持有业务状态。 界面控件里不存任何逻辑数据,只做渲染;刷新函数永远从 Model 取值。这样界面重建(开关面板、热更后刷新)不会丢数据。

第二,Model 不引用 View。 数据层出现 UI 对象引用是崩坏的开始,Model 只发事件/回调,谁关心谁来订阅。这保证了无界面环境(机器人压测、单元测试)下业务逻辑照样能跑。

第三,Controller 薄。 控制器只做参数校验、协议转换、流程编排,不堆业务算法;算法沉到 Model 的方法或独立模块里。Controller 越薄,热更替换越安全。

热更视角的额外红利

数据与视图分离后,热更界面逻辑(View/Controller)完全不必触碰运行中的 Model 数据,替换即生效、状态零丢失;反过来改数值规则也不必动界面代码。对需要高频更新活动的商业服来说,这个分离不是架构洁癖,而是直接的运维效率。从一个面板开始实践,把"回调里改三件事"的旧代码逐步拆干净,整套规范就能在团队里自然生长。

视图层

openwindows(actor, winID)

查看自己面板,Controller 触发面板刷新的入口

参数类型说明
actorobject玩家对象(必填参数)
winIDinteger101=装备 102=状态 103=属性 104=技能 105=生肖 106=称号 1011=时装(必填参数)

addbutton(actor, windowid, buttonid, icon)

增加自定义按钮,动态生成的视图元素由 Controller 统一挂载

参数类型说明
actorobject玩家对象(必填参数)
windowidinteger主窗口ID(必填参数)
buttonidinteger按钮ID(必填参数)
iconstring图标内容(必填参数)

delbutton(actor, windowid, buttonid)

删除自定义按钮,数据缩水时同步回收,否则残留点击区

参数类型说明
actorobject玩家对象(必填参数)
windowidstring主窗口ID(必填参数)
buttonidinteger按钮ID(必填参数)

数据到视图

sendredvartoclient(actor)

立即推送前端变量,Model 变化后只推变化的字段,不整页重建

参数类型说明
actorobject玩家对象(必填参数)

refreshitem(actor, item, type, position, value)

刷新物品信息到前端,列表类视图的局部刷新

参数类型说明
actorobject玩家对象(必填参数)
itemobject物品对象(必填参数)
typenumber[1,2,3](必填参数)
positionnumber当type=1,取值范围[0..49]type=2,取值范围[0..19](必填参数)
valuenumber设置物品对应位置值(必填参数)

状态标记

reddot(actor, win_id, btn_id, x, y, type, info)

给按钮增加红点,把状态转换成视图可见的红点,而不是在界面里判断业务

参数类型说明
actorobject玩家对象(必填参数)
win_idinteger窗口ID(必填参数)
btn_idinteger按钮ID/任务栏填任务ID(必填参数)
xintegerX坐标(必填参数)
yintegerY坐标(必填参数)
typeinteger红点模式0=图片1=特效(必填参数)

reddel(actor, win_id, btn_id)

给按钮删除红点,状态复位时清标记

参数类型说明
actorobject玩家对象(必填参数)
win_idinteger窗口ID(必填参数)
btn_idinteger按钮ID/任务栏填任务ID(必填参数)
作者履历与出处
本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理,讲解体系出自多年商业端开发生产一线。作者团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。
© 威海旷世互娱 · 返回文章地图 · 课程体系 · 幂尔框架