Skip to content

Commit 290c442

Browse files
committed
docs(lua): 添加Lua模块化编程速查表文档
新增Lua模块化编程的详细速查表,包含标准模板、调用方式、目录管理、面向对象风格等最佳实践
1 parent b16b7e8 commit 290c442

1 file changed

Lines changed: 228 additions & 0 deletions

File tree

docs/cheatsheet/lua/module.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
这是一份关于 Lua 模块化(Module)组织与编写的速查表(Cheatsheet)。Lua 的模块机制非常灵活,本质上是“利用 Table 和 Closure(闭包)来管理作用域”。
2+
3+
---
4+
5+
# Lua 模块化组织 Cheatsheet
6+
7+
## 1. 标准模板 (The Golden Standard)
8+
9+
这是目前 Lua (5.1+) 最推荐的模块写法。**不要使用** 已废弃的 `module()` 函数。
10+
11+
```lua
12+
-- 文件名: mymodule.lua
13+
local M = {} -- 1. 定义模块表
14+
15+
-- 2. 定义私有变量/函数 (Local)
16+
local default_scale = 1.5
17+
local function helper()
18+
return "I am hidden"
19+
end
20+
21+
-- 3. 定义公开变量/函数 (绑定到 M)
22+
M.version = "1.0"
23+
24+
function M.say_hello(name)
25+
-- 可以访问私有变量
26+
return "Hello " .. name .. ", scale: " .. default_scale
27+
end
28+
29+
function M.get_helper()
30+
return helper()
31+
end
32+
33+
-- 4. 返回模块表
34+
return M
35+
```
36+
37+
---
38+
39+
## 2. 调用模块
40+
41+
```lua
42+
-- main.lua
43+
local mymod = require("mymodule") -- 这里的字符串对应文件名(不带.lua)
44+
45+
print(mymod.version) -- 输出: 1.0
46+
print(mymod.say_hello("Lua"))
47+
-- print(mymod.helper()) -- 报错/nil,因为 helper 是 local 的
48+
```
49+
50+
---
51+
52+
## 3. 文件目录与路径管理
53+
54+
Lua 使用点号 `.` 来分隔目录,使用 `package.path` 来查找文件。
55+
56+
### 目录结构示例
57+
58+
```text
59+
project/
60+
├── main.lua
61+
├── config.lua
62+
└── utils/
63+
├── init.lua <-- 特殊文件
64+
├── math.lua
65+
└── string.lua
66+
```
67+
68+
### 引用方式
69+
70+
```lua
71+
local conf = require("config") -- 加载 config.lua
72+
local umath = require("utils.math") -- 加载 utils/math.lua
73+
local utils = require("utils") -- 加载 utils/init.lua (类似 index.js)
74+
```
75+
76+
### 什么是 `init.lua`?
77+
78+
`require("folder_name")` 时,Lua 会尝试查找 `folder_name/init.lua`。这允许你将一个文件夹作为一个整体模块导出。
79+
80+
**utils/init.lua 示例:**
81+
82+
```lua
83+
local M = {}
84+
M.math = require("utils.math")
85+
M.string = require("utils.string")
86+
return M
87+
```
88+
89+
---
90+
91+
## 4. 面向对象风格 (Class Module)
92+
93+
如果你需要模块作为一个“类”来生成实例:
94+
95+
```lua
96+
-- person.lua
97+
local Person = {}
98+
Person.__index = Person -- 元表索引指向自己
99+
100+
-- 构造函数
101+
function Person.new(name, age)
102+
local self = setmetatable({}, Person)
103+
self.name = name
104+
self.age = age
105+
return self
106+
end
107+
108+
-- 成员方法 (使用 : 语法)
109+
function Person:speak()
110+
print("My name is " .. self.name)
111+
end
112+
113+
return Person
114+
```
115+
116+
**使用:**
117+
118+
```lua
119+
local Person = require("person")
120+
local p1 = Person.new("Alice", 30)
121+
p1:speak()
122+
```
123+
124+
---
125+
126+
## 5. 高级技巧与坑点
127+
128+
### 避免全局污染 (Global Pollution)
129+
130+
**错误写法:**
131+
132+
```lua
133+
-- mymodule.lua
134+
function GlobalFunc() end -- 糟糕!这会污染全局环境 _G
135+
```
136+
137+
**正确写法:**
138+
始终在变量和函数前加 `local`,或者显式赋值给模块表 `M`
139+
140+
### 循环依赖 (Circular Dependencies)
141+
142+
如果 A require B,且 B require A,会导致栈溢出或返回 nil。
143+
144+
* **解决:** 将公共部分提取到模块 C,或者在一个模块内部通过 `local` 延迟加载。
145+
146+
### 重新加载模块 (Hot Reloading)
147+
148+
Lua 默认会缓存模块在 `package.loaded` 中,再次 `require` 不会重新执行文件。
149+
如果开发中需要热重载:
150+
151+
```lua
152+
function reload_module(module_name)
153+
package.loaded[module_name] = nil
154+
return require(module_name)
155+
end
156+
```
157+
158+
### 添加自定义搜索路径
159+
160+
如果你的模块不在标准路径下:
161+
162+
```lua
163+
-- 在 require 之前添加
164+
package.path = package.path .. ";./libs/?.lua;./src/?.lua"
165+
```
166+
167+
---
168+
169+
## 6. 几种常见的写法变体
170+
171+
### 变体 A: 尾部返回 (最常用)
172+
173+
```lua
174+
local M = {}
175+
function M.foo() end
176+
return M
177+
```
178+
179+
### 变体 B: 局部函数导出 (性能稍好)
180+
181+
这种写法在文件内部调用 `foo` 时稍微快一点(因为是 local 调用),最后统一导出。
182+
183+
```lua
184+
local function foo() end
185+
local function bar() end
186+
187+
return {
188+
foo = foo,
189+
bar = bar
190+
}
191+
```
192+
193+
### 变体 C: 直接返回函数 (单一职责)
194+
195+
如果模块只做一件事:
196+
197+
```lua
198+
-- logger.lua
199+
return function(msg)
200+
print("[LOG]: " .. msg)
201+
end
202+
203+
-- 使用
204+
local log = require("logger")
205+
log("Something happened")
206+
```
207+
208+
---
209+
210+
## 7. 模块查找顺序
211+
212+
当执行 `require("mod")` 时,Lua 按以下逻辑查找:
213+
214+
1. 检查 `package.loaded["mod"]` 是否已有缓存。
215+
2. 检查 `package.preload["mod"]` 是否有预加载器。
216+
3. 搜索 `package.path` (Lua 文件)。
217+
* `mod.lua`
218+
* `mod/init.lua`
219+
4. 搜索 `package.cpath` (C 库 .so/.dll)。
220+
221+
---
222+
223+
## 总结最佳实践
224+
225+
1. **文件即模块**:一个文件对应一个模块。
226+
2. **Local First**:所有变量默认 `local`,只有需要导出的才放入返回表。
227+
3. **返回 Table**:文件末尾始终 `return M`
228+
4. **无副作用**`require` 一个模块不应产生打印日志或修改全局变量等副作用,只应返回定义。

0 commit comments

Comments
 (0)