Lua的模块系统基于表和require函数实现。模块本质上是一个返回表的Lua文件。require函数负责加载模块、缓存结果、处理依赖关系。模块加载过程包括:搜索模块文件、编译/执行模块代码、缓存返回值。这种设计使得模块只加载一次,提高了性能并避免了重复初始化的问题。
模块是一个包含函数和变量的表,用于组织和重用代码。Lua使用require函数加载模块。
-- 模块的基本概念
-- 模块 = 表 + 函数 + 变量
-- 模块文件通常以.lua结尾
-- 模块名应该使用小写字母和下划线
-- 模块的优势:
-- 1. 代码组织和封装
-- 2. 避免全局命名空间污染
-- 3. 代码重用
-- 4. 依赖管理
-- 5. 版本控制
-- mymath.lua
local mymath = {}
function mymath.add(a, b)
return a + b
end
function mymath.subtract(a, b)
return a - b
end
function mymath.multiply(a, b)
return a * b
end
function mymath.divide(a, b)
if b ~= 0 then
return a / b
else
return nil, "除数不能为0"
end
end
return mymath
-- main.lua
local mymath = require("mymath")
print(mymath.add(10, 5)) -- 15
print(mymath.multiply(3, 4)) -- 12
local result, err = mymath.divide(10, 0)
if result then
print(result)
else
print(err) -- 除数不能为0
end
-- utils.lua
local M = {}
-- 私有函数(不导出)
local function privateFunc()
return "这是私有函数"
end
-- 公有函数
function M.trim(str)
return string.gsub(str, "^%s*(.-)%s*$", "%1")
end
function M.split(str, delimiter)
local result = {}
local pattern = string.format("([^%s]+)", delimiter)
for word in string.gmatch(str, pattern) do
table.insert(result, word)
end
return result
end
function M.isEmpty(str)
return str == nil or str == ""
end
return M
require函数的加载过程:1) 检查package.loaded缓存,如果存在则直接返回;2) 在package.path中搜索.lua文件;3) 在package.cpath中搜索.so/.dll等C模块;4) 如果找到文件,加载并执行;5) 将返回值存入package.loaded缓存;6) 返回模块。这种机制确保模块只加载一次,并支持循环依赖检测。
-- require会在以下路径搜索模块
print(package.path)
-- 输出类似: ./?.lua;/usr/local/share/lua/5.4/?.lua;...
-- 加载模块
local json = require("json") -- 查找json.lua
-- 加载子目录中的模块
local db = require("lib.database") -- 查找lib/database.lua
-- 模块只加载一次,再次require返回缓存
local m1 = require("mymodule")
local m2 = require("mymodule")
print(m1 == m2) -- true (同一个表)
-- 查看已加载的模块
for k, v in pairs(package.loaded) do
print(k, v)
end
-- 重新加载模块
package.loaded["mymodule"] = nil
local m3 = require("mymodule") -- 重新加载
-- require的错误处理
local ok, module = pcall(require, "nonexistent")
if not ok then
print("模块加载失败:", module)
end
-- 模块初始化参数
local mymodule = require("mymodule", {config = "value"})
-- 预加载模块(在require之前)
package.preload["mymodule"] = function()
return {value = "preloaded"}
end
local preloaded = require("mymodule")
print(preloaded.value) -- preloaded
-- 查看模块搜索路径
print(package.path)
-- 添加自定义搜索路径
package.path = package.path .. ";/my/lua/libs/?.lua"
-- 查看C模块搜索路径
print(package.cpath)
-- 设置搜索路径
package.path = "./?.lua;./lib/?.lua;" .. package.path
-- person.lua
local Person = {}
Person.__index = Person
function Person.new(name, age)
local self = setmetatable({}, Person)
self.name = name
self.age = age
return self
end
function Person:sayHello()
print("你好,我是" .. self.name .. ",今年" .. self.age .. "岁")
end
function Person:birthday()
self.age = self.age + 1
print(self.name .. "过生日了,现在" .. self.age .. "岁")
end
return Person
-- 使用
local Person = require("person")
local p = Person.new("张三", 25)
p:sayHello()
p:birthday()
-- config.lua
local config = {
database = {
host = "localhost",
port = 3306,
user = "root",
password = "123456"
},
server = {
host = "0.0.0.0",
port = 8080
}
}
function config.getDatabaseUrl()
return string.format("%s:%d", config.database.host, config.database.port)
end
return config
-- 使用
local config = require("config")
print(config.database.host)
print(config.getDatabaseUrl())
LuaRocks是Lua的包管理器,类似于Python的pip或Node.js的npm。它管理模块的下载、安装、依赖解析和版本控制。LuaRocks使用rockspec文件描述包的元数据和依赖关系。安装的包通常放在系统目录或用户目录下,LuaRocks会自动更新package.path和package.cpath以包含这些目录。
-- 安装LuaRocks
-- macOS: brew install luarocks
-- Linux: sudo apt-get install luarocks
-- Windows: 下载安装包
-- 安装包
luarocks install luasocket
luarocks install lua-cjson
luarocks install lpeg
-- 指定版本安装
luarocks install luaunit 3.3-1
-- 从本地安装
luarocks install ./myrock-1.0-1.rockspec
-- 使用安装的包
local socket = require("socket")
local cjson = require("cjson")
-- 列出已安装的包
luarocks list
-- 搜索包
luarocks search json
-- 查看包信息
luarocks show lua-cjson
-- 卸载包
luarocks remove lua-cjson
-- 更新包
luarocks install lua-cjson --force-reinstall
-- 创建rockspec文件
-- mymodule-1.0-1.rockspec
package = "mymodule"
version = "1.0-1"
source = {
url = "git://github.com/user/mymodule.git",
tag = "v1.0"
}
description = {
summary = "My awesome module",
detailed = [[
A detailed description of the module.
]],
homepage = "http://www.example.com/mymodule",
license = "MIT/X11"
}
dependencies = {
"lua >= 5.1, < 5.5"
}
build = {
type = "builtin",
modules = {
mymodule = "src/mymodule.lua"
}
}
-- 模块模式1:返回表(推荐)
local M = {}
function M.foo() return "foo" end
return M
-- 模块模式2:使用module函数(已弃用)
-- module("mymodule", package.seeall)
-- 模块模式3:单例模式
local Singleton = {}
local instance = nil
function Singleton.getInstance()
if not instance then
instance = {
value = 0,
increment = function(self)
self.value = self.value + 1
return self.value
end
}
end
return instance
end
return Singleton
-- 模块模式4:工厂模式
local Factory = {}
function Factory.create(type)
if type == "circle" then
return {type = "circle", area = function(r) return math.pi * r * r end}
elseif type == "square" then
return {type = "square", area = function(s) return s * s end}
end
end
return Factory
-- 模块模式5:混入模式
local Mixin = {}
function Mixin.addLogger(obj)
function obj.log(self, msg)
print("[LOG]", msg)
end
return obj
end
function Mixin.addSerializer(obj)
function obj.serialize(self)
local result = {}
for k, v in pairs(self) do
if type(v) ~= "function" then
result[k] = v
end
end
return result
end
return obj
end
return Mixin
-- 带依赖的模块
local M = {}
-- 声明依赖
local json = require("cjson")
local socket = require("socket")
function M.sendData(url, data)
local body = json.encode(data)
-- 使用socket发送数据
return body
end
function M.receiveData(jsonString)
return json.decode(jsonString)
end
return M
-- 延迟加载依赖
local M = {}
local json
local function getJson()
if not json then
json = require("cjson")
end
return json
end
function M.parse(str)
return getJson().decode(str)
end
return M
-- 循环依赖处理
-- moduleA.lua
local M = {}
local moduleB
function M.foo()
if not moduleB then
moduleB = require("moduleB")
end
return moduleB.bar()
end
return M
-- moduleB.lua
local M = {}
local moduleA = require("moduleA")
function M.bar()
return "bar from B"
end
return M
-- logger.lua
local logger = {}
local levels = {
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4
}
logger.level = levels.INFO
local function log(level, message)
if level >= logger.level then
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
local levelName = ""
for k, v in pairs(levels) do
if v == level then
levelName = k
break
end
end
print(string.format("[%s] [%s] %s", timestamp, levelName, message))
end
end
function logger.debug(message)
log(levels.DEBUG, message)
end
function logger.info(message)
log(levels.INFO, message)
end
function logger.warn(message)
log(levels.WARN, message)
end
function logger.error(message)
log(levels.ERROR, message)
end
function logger.setLevel(level)
logger.level = levels[level] or levels.INFO
end
return logger
-- 使用
local logger = require("logger")
logger.setLevel("DEBUG")
logger.debug("调试信息")
logger.info("普通信息")
logger.warn("警告信息")
logger.error("错误信息")