lua 热更新原理(一)

转:https://blog.youkuaiyun.com/zxm342698145/article/details/80613429

热更一般是针对模块的线上替换。我们动态加载要热更的模块试试。例如,我们在循环间隔加载某模块,这样就能有时间去更改模块了。测试的代码如下:

for i = 1, 5 do
    local m = require('other')
    print('other.a is ', m.a)
    Sleep(5)
end
我们循环加载了other这个模块他仅仅返回一个local table {a = 100},在sleep(lua没有直接提供sleep,我们可以利用os模块自己实现)期间我们改变other模块a的值为101,不出意外,m.a还是100。这是为什么呢?

这是由于require缓存机制决定的,请参考lua require机制

要想打破require缓存机制,我们必须删除缓存,package.loaded[module_name] = nil就可以做到。

我们将package.loaded['other'] = nil加在第四行后,再改变a的值,不出意外,a的值被改变了。

还有一个问题就是,之前的旧数据可能被其他变量引用,所以旧数据仍然有可能存在,例如下面的代码:

local g = {}

for i = 1, 5 do
    local m = require('other')
    if (i == 1) then g = m end
    print('other.a is ', m.a)
    Sleep(5)
    package.loaded['other'] = nil
end
由于g第一次引用的是旧数据,最后打印g.a时还是100,而不是修改之后的值。我们画个示意图就能很好的说明问题:

左边是lua全局表package.loaded,require模块就会填入其中。再次require时我们发现他已经指向另一个表了,而g仍然指向之前的表数据。

怎么才能让g指向新的表数据,直接让g指向他不行吗?不太可行,因为在实际的项目中不太确定有哪些变量引用到了旧的数据,就算知道,那也是侵入式的修改了。另一种方法就是package.loaded仍引用旧的数据表,只不过与新的数据表交换数据。还是画个示意图:

有了上面的描述,相信代码实现很简单:

function reload_module(module_name)
    local old_module = package.loaded[module_name] or {}
    package.loaded[module_name] = nil
    require (module_name)

    local new_module = package.loaded[module_name]
    for k, v in pairs(new_module) do
        old_module[k] = v
    end

    package.loaded[module_name] = old_module
    return old_module
end
以上有个问题就是模块返回的最好是表数据,这样才能替换已经引用到的旧数据,例如g。

还有,如果要热更的模块中有全局变量,热更模块时修改了全局变量,那么在_G['other']指向的值也会被覆盖,这没什么问题,本来全局变量就应该被覆盖才对。但是在热更之前如果有变量指向这个全局变量就糟糕了,这个变量也应该被修改才对。所以类似上面的交换新旧package.loaded['other']数据,全局数据也应该交换才对。可惜的是,没有办法知道全局变量来自哪个模块,所以无法做到替换该模块的全局变量。不知道大佬们有没有办法。
 

### Lua热更新实现案例与示例代码 Lua热更新种在不中断程序运行的情况下,动态替换或重新加载模块的技术。以下是关于Lua热更新的实现方法和示例代码。 #### 热更新的基本原理Lua中,热更新通常通过重新加载模块并更新全局环境中的引用实现。以下是个典型的热更新逻辑[^4]: 1. 将目标模块从`package.loaded`表中移除。 2. 使用`require`重新加载模块。 3. 更新旧模块中的键值对以确保所有引用都指向最新的实现。 #### 示例代码:Lua热更新实现 以下是个完整的Lua热更新实现案例: ```lua -- test.lua module(..., package.seeall) function func(a, b) return a * b -- 初始实现为乘法 end ``` ```lua -- main.lua require "test" function mainloop() local ret = test.func(10, 10) print("Result:", ret) -- 输出结果 end function reload_module(module_name) local old_module = _G[module_name] package.loaded[module_name] = nil -- 移除已加载的模块 require(module_name) -- 重新加载模块 local new_module = _G[module_name] for k, v in pairs(new_module) do old_module[k] = v -- 更新旧模块的键值对 end package.loaded[module_name] = old_module -- 恢复到全局环境 end function reload() local modules_to_reload = {"test"} -- 需要热更新的模块列表 for _, module in ipairs(modules_to_reload) do reload_module(module) end end -- 测试热更新 mainloop() -- 调用初始版本的函数 -- 修改 test.lua 中的 func 函数为加法后保存文件 reload() -- 执行热更新 mainloop() -- 再次调用函数,验证热更新是否生效 ``` #### 运行流程说明 1. 初始状态下,`test.lua`中的`func`函数实现为乘法(`a * b`)。 2. 调用`mainloop`函数,输出结果为`100`。 3. 修改`test.lua`中的`func`函数实现为加法(`a + b`),并保存文件。 4. 调用`reload`函数执行热更新。 5. 再次调用`mainloop`函数,输出结果为`20`,证明热更新成功。 #### 注意事项 - 热更新时需要确保所有引用该模块的地方都被正确更新,否则可能导致程序行为异常。 - 在复杂项目中,建议结合调试工具(如EmmyLua)进行开发和测试[^3]。 #### 参考项目 如果需要更复杂的热更新方案,可以参考以下项目: - `lua_hotupdate`项目提供了Lua热更新的完整实现案例[^1]。 - 项目地址:[https://gitcode.com/gh_mirrors/lu/lua_hotupdate](https://gitcode.com/gh_mirrors/lu/lua_hotupdate) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值