最近准备开始写个系列分类叫五分钟技术分享,就是单章内容控制在五分钟适合碎片化的时间浏览。本期分享的主题是Lua语言中Require的工作原理

require从哪儿加载文件?

package.path 拼接当前require的路径进行查找,会从输出结果中挨个访问是否存在该文件,如果有就会通过loader(加载器)加载。

1
2
3
print(package.path) --Windows测试环境

--;.\?.lua;C:\Program Files (x86)\Lua\5.1\lua\?.lua;C:\Program Files (x86)\Lua\5.1\lua\?\init.lua;C:\Program Files (x86)\Lua\5.1\?.lua;C:\Program Files (x86)\Lua\5.1\?\init.lua;C:\Program Files (x86)\Lua\5.1\lua\?.luac

将上面的?替换为文件名,就是尝试加载的文件地址,多个分号分隔代表会从多个路径进行优先级查找。

require的模块存在哪儿?

require的模块都存储在package.loaded。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require "main"

for k, v in pairs(package.loaded) do
print("loaded:" .. k .. "")
end

-- loaded:string
-- loaded:debug
-- loaded:package
-- loaded:_G
-- loaded:io
-- loaded:os
-- loaded:table
-- loaded:math
-- loaded:coroutine
-- loaded:main 这边出现了main文件名

require的唯一性

观察下面代码我们定义一个main.lua文件

1
2
3
4
5
main = {}

print("load file main")

return main --缺省情况下默认return true

运行下面的代码会得到输出:

1
2
3
4
5
6
7
8
local ma1 = require "main"
print(ma1)
local ma2 = require "main"
print(ma2)

-- load file main
-- table: 00EA98E8
-- table: 00EA98E8

可以看到 “load file main” 并不是每次被require的时候都会执行。只有在首次执行会输出。

让require的模块执行多次

默认没有return的情况下会走一个return true,lua会自己确保当前文件中执行函数只走一次就被缓存。
在显式添加return false的情况下,才会多次require每次都刷新package.loaded缓存过的数据。

1
2
3
4
5
6
7
8
9
10
11
main = {}
print("load file main")
return false


在调用的文件中:
require "main"
require "main"
输出:
load file main
load file main

还有另外一种方式就是将package.loaded.main设置为nil,require的时候会发现loaded中存储的main找不到了,就会再次走加载流程。

1
2
3
4
5
6
7
8
9
10
11
12
main = {}
print("load file main")
return main


在调用的文件中:
require "main"
package.loaded.main = nil
require "main"
输出:
load file main
load file main