lua 热更新原理(一)

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

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']数据,全局数据也应该交换才对。可惜的是,没有办法知道全局变量来自哪个模块,所以无法做到替换该模块的全局变量。不知道大佬们有没有办法。



参考:

https://blog.csdn.net/xufeng0991/article/details/52473602













猜你喜欢

转载自blog.csdn.net/zxm342698145/article/details/80613429