一个模块文件
-- mymath.lua
local M = {}
function M.add(a, b) return a + b end
function M.sub(a, b) return a - b end
local PI = 3.14159 -- 模块私有(不导出)
return M
要点:
- 顶层
local M = {} - 把要导出的挂
M.xxx - 末尾
return M
用它
-- main.lua
local math2 = require("mymath")
print(math2.add(1, 2)) -- 3
-- 别名
local M = require("mymath")
print(M.sub(5, 3)) -- 2
require 返回模块导出的表。
require 的查找规则
Lua 按 package.path 模式查找:
print(package.path)
-- "./?.lua;/usr/local/share/lua/5.4/?.lua;..."
? 是模块名占位符。require("foo.bar") → 尝试 ./foo/bar.lua、/usr/local/share/lua/5.4/foo/bar.lua...
第一个找到的胜出。
加 / 改 package.path
-- 把当前目录的 mylib/ 加进搜索路径
package.path = "./mylib/?.lua;" .. package.path
或环境变量 LUA_PATH(构建系统常用)。
require 只执行一次
local a = require("foo")
local b = require("foo")
print(a == b) -- true(同一个表)
模块执行结果缓存在 package.loaded:
-- 强制重载(开发用)
package.loaded["foo"] = nil
local foo = require("foo")
子目录 / 嵌套
project/
├── main.lua
└── lib/
├── parser.lua
└── util/
└── math.lua
-- 需要先把 lib/ 加到 package.path
package.path = "./lib/?.lua;./lib/?/init.lua;" .. package.path
local parser = require("parser")
local m = require("util.math") -- 点号 = 路径分隔
util.math → 查找 ./lib/util/math.lua 或 ./lib/util/math/init.lua。
模块里的 require
-- mylib.lua
local utils = require("utils") -- 加载兄弟模块
local M = {}
function M.process(x) return utils.clean(x) end
return M
完全和 main 里一样写。
C 模块(动态库)
require("foo") 也会查 package.cpath:
print(package.cpath)
-- "./?.so;/usr/local/lib/lua/5.4/?.so;..."
C 写的 Lua 模块编译成 .so/.dll,require 加载共享库 + 调入口函数 luaopen_foo。
例:luarocks install lpeg 装一个 C 模块,然后 require("lpeg") 直接用。
详见 第 24 篇嵌入 C。
单文件库的发布模式
-- inspect.lua(来自 https://github.com/kikito/inspect.lua)
local inspect = {}
-- ... 几百行实现 ...
return inspect
下载一个文件,扔项目里 require 即可——很多 Lua 库这样发布。
避免循环依赖
-- a.lua
local b = require("b")
local M = {x = b.y}
return M
-- b.lua
local a = require("a") -- 循环!
local M = {y = "hi"}
return M
Lua 检测到循环时会返回部分初始化的表——通常导致 nil 错误。
解决:重构——拆出共同依赖到第三个模块;或将引用延后到函数体内:
-- a.lua
local M = {}
function M.process(x)
local b = require("b") -- 延迟加载
return b.f(x)
end
return M
模块约定
- 文件名 = 模块名
- 一律返回 table(除非有特殊原因返回函数)
- 不污染全局(不写
function foo()没 local) - 文件顶部声明所有 require
→ 下一篇 错误处理