今天我们继续学习lua里的模块还有包

首先说一下这两个东西的概念

模块(module)

Lua 的模块是由变量、函数等已知元素组成的 table,然后把需要导出的常量、函数放入其中

特殊之处就是模块返回值 就是这个表

1
2
3
4
5
6
7
8
9
10
11
12
-- utils.lua
local utils = {}

function utils.add(a, b)
return a + b
end

function utils.sub(a, b)
return a - b
end

return utils

但是怎么用呢,其实,Lua 模块 ≈ C# 的 命名空间 + 静态类 + 单例实例

相同含义的C#语句就是

1
2
3
4
5
6
7
8
namespace Utils
{
public static class Math
{
public static int Add(int a, int b) => a + b;
public static int Sub(int a, int b) => a - b;
}
}

使用就是不需要实现类的实例,直接using引入命名空间然后点语法使用里面的属性就行

1
2
3
4
5
6
7
8
9
10
using Utils;

class Program
{
static void Main()
{
int result = Math.Add(1, 2);
Console.WriteLine(result);
}
}

这里在lua中的使用方法就是使用require函数

require 函数

要使用文件使用一个模块使用require函数

1
require("<模块名>")

或者

1
require "<模块名>"

执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}

-- 定义一个常量
module.constant = "这是一个常量"

-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end

local function func2()
print("这是一个私有函数!")
end

function module.func3()
func2()
end

return module

然后我们在另外一个文件启用

1
2
3
4
5
6
7
-- test_module.lua 文件
-- module 模块为上文提到到 module.lua
require("module")

print(module.constant)

module.func3()

image-20260413202737035

image-20260413202759729

但是大家可能会遇到的误区就是误以为模块里定义的表名就是文件名,其实不是的,

此时我们就可以把这个module.lua当做命名空间,然后modele.lua里面的属性和函数视作C#里的静态static函数,使用直接用点语法就行

image-20260413203756002

image-20260413203812059
require函数的原理

当你写:

1
require("module")

Lua 会做一件事: 到一堆“固定路径”里去找 module.lua,也就是它会尝试从 Lua 文件或 C 程序库中加载模块

require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH(这里我用的是我自定义的LUA_HOME,因为我的是lua5.4) 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。

其实就是我们设置的path路径

image-20260413212716317

lua会去这个路径package.path去找路径

image-20260413211400410

而在这些路径里

1
D:\ZeroBraneStudio\lua-5.4.2_Win64_bin\lua\?.lua;D:\ZeroBraneStudio\lua-5.4.2_Win64_bin\lua\?\init.lua;D:\ZeroBraneStudio\lua-5.4.2_Win64_bin\?.lua;D:\ZeroBraneStudio\lua-5.4.2_Win64_bin\?\init.lua;D:\ZeroBraneStudio\lua-5.4.2_Win64_bin\..\share\lua\5.4\?.lua;D:\ZeroBraneStudio\lua-5.4.2_Win64_bin\..\share\lua\5.4\?\init.lua;.\?.lua;.\?\init.lua;D:\ZeroBraneStudio\lua-5.1.5-52\5.1\lua\?.luac  

可以看到有很多问号“?”,而问号就是占位符

使用require(“module”)函数会把问号替换为module,然后lua会开始查找这个moule.lua文件,然后转换为类似执行过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 1. 看缓存
if package.loaded["module"] then
return package.loaded["module"]
end

-- 2. 找文件路径
local path = "./module.lua"

-- 3. 加载文件
local f = loadfile(path)

-- 4. 执行文件
local result = f()

-- 5. 缓存
package.loaded["module"] = result

-- 6. 返回
return result

而返回值就是刚才的模块里的表

包(package)

Lua里有一个全局变量:package

它其实是一个大工具箱,里面专门负责:

  • 去哪里找模块
  • 怎么加载模块
  • 加载过的要不要再加载

主要的四个功能就是

① package.path (控制 Lua 模块搜索路径)

控制:Lua 去哪里找 .lua 文件

② package.loaded (缓存机制)

控制:模块是否重复加载

③ package.cpath(C模块)

用来找 .dll / .so

④ package.preload(虚拟模块)

手动注册模块(不需要文件)