一、什么是模块
模块就是一些代码(可以是 Lua 编写的,也可以是 C 语言编写的),这些代码可以通过函数 require
加载,然后创建和返回一个表,这个表就类似命名空间。
所有的标准库都是模块,例如 math
、 string
模块
使用表来承载模块,有很显著的优点,可以像操作普通表一样操作模块,而且能利用 Lua 语言的所有功能实现额外的功能。
例如引入 math
模块
-- 两种书写方式都可以使用
local math = require "math"
--local math = require("math")
-- sin 使用的是弧度,不是角度
print(math.sin(3.14))
也可以直接引入模块中的一个函数,例如以下代码
直接引入模块中的函数,实际上只是省去了模块这一中间变量,从加载的模块 table 中获取相应的 value
-- 引用模块中的某个函数
-- 等同于 require("math").sin
-- 此时 require("math") 获取到了引入表,`.sin` 即从表中获取了对应的值 value ,此时为一个函数
local sin = require "math".sin
print(sin(3.14))
二、require(modname)
Lua 通过 require(modname)
函数进行加载模块,modname
为需要加载的模块名(字符串类型)。
0、require 函数加载模块的流程
require 会先从 package.loaded
中获取,如果没有找到相应模块,则进入根据搜索器列表 package.searches
中设置的搜索器按顺序进行查找。
package.searches
默认内置了 4 个搜索器,按顺序分别为 预加载搜索器
、Lua 搜索器
、C 标准库搜索器
、C 库子模块搜索器
。
假设我们使用了 require('A')
进行加载 A 模块,会进行以下加载步骤:
1、第一步:会在 package.loaded
中检查模块 “A” 是否已经存在,如果存在则会将其返回,不存在则进入第二步骤
package.loaded
是一个 table , 存储着加载成功的模块,以模块名为 key ,模块返回结果为 value 的形式存放。
如果 package.loaded
不存在对应的模块,则会进入到后续的步骤进行搜索,无论后续的哪一步骤让模块加载成功,都会将模块的返回值(该返回值类型可以是 function 、 table 等数据类型)作为 value 和加载的模块名(例如这里的 A
)为 key ,以 key-value 的形式存放到 package.loaded
table 中。如果模块没有返回值,则会用 true 代替返回值,从而达到不会每次加载相同模块都需要运行一次加载流程。
举个例子
加载两个模块,然后通过打印 package.loaded
查看已经加载的模块
print("package.loaded 已经加载的模块:")
-- 获取当前 lua 的文件夹路径
local currentPath = debug.getinfo(1, "S").source:sub(2):match("(.*/)")
-- 设置加载模块路径
package.path = package.path .. ";" .. currentPath .. "../?.lua"
require("一个合理的模块")
require("module.sub")
for path, package in pairs(package.loaded) do
print("----- 模块【" .. path .. "】包含的属性:-----")
if type(package) == "table" then
for key, value in pairs(package) do
print(key, "---", value)
end
else
print(path, "---", package)
end
print("--------------------------------")
end
打印的内容会比较多,因为 Lua 默认加载的函数也会在其中,但在众多的输出中,可以找到加载的 “一个合理的模块” 和 “module.sub” 模块(见下图)
“一个合理的模块” 模块会返回一个 table ,所以会将 table 存储在 package.loaded
表中(这里输出的便是存储的内容)。
具体代码可以进入 github 查看 https://github.com/zincPower/lua_study_2022/blob/master/11%20%E6%A8%A1%E5%9D%97%E5%92%8C%E5%8C%85/%E4%B8%80%E4%B8%AA%E5%90%88%E7%90%86%E7%9A%84%E6%A8%A1%E5%9D%97.lua
“module.sub” 模块则没有返回值,所以 Lua 会默认返回 true ,将其存储在 pacakge.loaded
表中(从输出的内容也可以验证这一点)。
具体代码可以进入 github 查看 https://github.com/zincPower/lua_study_2022/blob/master/11%20%E6%A8%A1%E5%9D%97%E5%92%8C%E5%8C%85/module/sub.lua
2、第二步:在 “预加载搜索器” 中使用 package.preload
查找是否有对应加载函数,如果有则会将加载函数返回,否则进入第三步骤
package.preload
也是一个 table ,只是他的 value 必须是一个加载函数。
会根据 require 传入的模块名,在 preload 中查询,如果找到对应的 key ,则调用 value(是一个函数),会将请求的模块名和加载的来源(这里是通过预加载器,即 preload)传递给 value 函数,最后会将该函数的返回值作为模块的返回值存储在第一步提到的 package.loaded 中, 方便后续加载相同的模块。
可以运行代码,通过调用 showLoadedModule 函数,感受这一过程
print("package.preload:")
local function