强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

10 - 模块与包 / Modules & Packages

模块与包 / Modules & Packages

Lua 的模块系统极其简单——一个模块就是一个返回 table 的文件。通过 require 加载,通过 package.path 查找。

Lua’s module system is extremely simple — a module is a file that returns a table. Loaded with require, found via package.path.


🟢 基础 / Basics — 创建和使用模块

1. 创建模块 / Creating a Module

-- math_utils.lua — 一个简单的模块
local M = {}

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

function M.multiply(a, b)
    return a * b
end

function M.factorial(n)
    if n <= 1 then return 1 end
    return n * M.factorial(n - 1)
end

return M    -- 返回模块 table

2. 使用模块 / Using a Module

-- main.lua
local math_utils = require("math_utils")    -- 加载模块

print(math_utils.add(3, 5))           -- 8
print(math_utils.multiply(4, 7))      -- 28
print(math_utils.factorial(5))        -- 120

-- require 的搜索路径:
-- 1. package.loaded["math_utils"](已加载则直接返回)
-- 2. package.preload["math_utils"](预加载器)
-- 3. package.path 中的文件(.lua 文件)
-- 4. package.cpath 中的文件(.so/.dll 文件)

3. 模块路径 / Module Paths

-- 查看当前搜索路径
print(package.path)     -- .lua 文件搜索路径
print(package.cpath)    -- .so/.dll 文件搜索路径

-- 默认路径示例(Linux):
-- ./?.lua;/usr/local/share/lua/5.4/?.lua;/usr/local/share/lua/5.4/?/init.lua

-- require("foo") 会搜索:
-- ./foo.lua
-- /usr/local/share/lua/5.4/foo.lua
-- /usr/local/share/lua/5.4/foo/init.lua

-- 自定义路径
package.path = package.path .. ";./lib/?.lua;./lib/?/init.lua"

-- 点号转目录分隔符
-- require("lib.utils")  →  搜索 lib/utils.lua

🟡 进阶 / Intermediate — 模块的高级用法

1. 模块缓存 / Module Caching

-- require 会缓存已加载的模块
-- require caches loaded modules in package.loaded

local m1 = require("math_utils")
local m2 = require("math_utils")

print(m1 == m2)    -- true(同一个 table 引用)

-- 强制重新加载
package.loaded["math_utils"] = nil
local m3 = require("math_utils")
print(m1 == m3)    -- false(新的 table)

-- 查看已加载的模块
for name, mod in pairs(package.loaded) do
    print(name, type(mod))
end

2. 模块的内部私有性 / Module Privacy

-- 推荐模式:用 local 变量做私有成员
local M = {}

-- 私有变量(不暴露给外部)
local counter = 0
local function privateHelper()
    counter = counter + 1
    return counter
end

-- 公开接口
function M.getCount()
    return counter
end

function M.increment()
    return privateHelper()
end

return M

-- 外部只能通过 M 的公开方法访问
-- counter 和 privateHelper 对外部不可见

3. module() 函数(已废弃)/ Deprecated module()

-- Lua 5.1 曾有 module() 函数,现在不推荐使用
-- module("mymodule")    -- 不要这样写!

-- 现代写法(简单明了):
local M = {}
-- ... 定义函数 ...
return M

4. Lua 调用 C 模块 / Lua Calling C Modules

-- C 模块通常编译为 .so 或 .dll 文件
-- 例如:local cjson = require("cjson")

-- C 模块的接口:
-- 1. 编写 C 代码,使用 Lua C API
-- 2. 编译为共享库 (.so)
-- 3. 放入 package.cpath 路径
-- 4. require("cjson") 即可

-- 查看可用的 C 模块路径
print(package.cpath)

-- 完整的 C 模块加载流程:
-- require("cjson")
-- 1. 检查 package.loaded["cjson"]
-- 2. 检查 package.preload["cjson"]
-- 3. 搜索 package.cpath,找到 cjson.so
-- 4. dlopen("cjson.so")
-- 5. 调用 luaopen_cjson() 函数
-- 6. 将返回值存入 package.loaded["cjson"]

🔴 高级 / Advanced — 沙箱与高级模块系统

1. 沙箱环境 / Sandbox Environment

-- 沙箱:限制代码可以访问的环境
local function createSandbox()
    local env = {
        print = print,        -- 允许 print
        tostring = tostring,
        tonumber = tonumber,
        type = type,
        pairs = pairs,
        ipairs = ipairs,
        string = string,
        math = math,
        table = table,
        -- 不暴露:io, os, debug, loadfile, dofile, require
    }
    return env
end

local function runSandboxed(code, env)
    local fn, err = load(code, "sandbox", "t", env)
    if not fn then return nil, err end
    return pcall(fn)
end

-- 使用
local sandbox = createSandbox()
local ok, err = runSandboxed([[
    print("Hello from sandbox!")
    local x = math.sqrt(16)
    print("sqrt(16) = " .. x)
    os.execute("rm -rf /")    -- 这行会报错:os 未定义
]], sandbox)

2. 环境隔离 / Environment Isolation

-- 每个模块可以有自己的环境
local function isolatedModule(code)
    local env = setmetatable({}, {__index = _G})    -- 继承全局环境
    local fn = load(code, "module", "t", env)
    fn()
    return env
end

local mod = isolatedModule([[
    local x = 10
    function hello() print("Hello!") end
    export_x = x    -- 导出到环境
]])

print(mod.export_x)    -- 10
-- mod.hello()         -- 可以调用

3. 模块热重载 / Hot Reload

-- 实现简单的模块热重载
local function reload(name)
    -- 清除缓存
    package.loaded[name] = nil
    
    -- 可选:清除 package.searchpath 找到的文件的缓存
    -- 重新 require
    local ok, mod = pcall(require, name)
    if ok then
        print("Reloaded: " .. name)
        return mod
    else
        print("Failed to reload " .. name .. ": " .. tostring(mod))
        return nil
    end
end

-- 使用场景:开发时修改模块后无需重启程序
-- local mylib = reload("mylib")

4. 模块初始化模式 / Module Initialization Patterns

-- 模式一:立即初始化
local M = {}
local config = {}    -- 模块加载时立即初始化
function M.init() end
return M

-- 模式二:延迟初始化(首次使用时初始化)
local M = {}
local initialized = false
local function ensureInit()
    if initialized then return end
    initialized = true
    -- 初始化操作
end
function M.doSomething()
    ensureInit()
    -- ...
end
return M

-- 模式三:工厂模式(每次 require 返回新实例)
local function create()
    local state = {}
    local M = {}
    function M.get() return state end
    function M.set(v) state = v end
    return M
end
return create    -- 返回工厂函数,而不是模块本身
-- 使用:local mymod = require("mymod")()

小结 / Summary

层级你需要知道的 / What You Need to Know
🟢 基础local M = {} … return M、require()、package.path
🟡 进阶模块缓存机制、package.loaded、私有变量、C 模块加载
🔴 高级沙箱环境、环境隔离、热重载、初始化模式

下一章:协程 / Coroutines