▒ 目录 ▒
🛫 导读
需求
本教程面向需要进行Lua代码模块化开发的开发者,旨在解决以下问题:理解Lua模块的本质与作用,掌握模块的创建、导出、导入方法,学会配置模块路径与处理依赖关系,最终通过模块化实现代码复用、功能隔离和项目结构优化,适用于中小型Lua项目的架构设计与维护。
开发环境
版本号 | 描述 | |
---|---|---|
文章日期 | 2025-08-30 | |
IDE | zbstudio.exe | lua 5.1 |
1️⃣ 模块基础:理解Lua模块的本质
Lua模块是实现代码模块化的核心机制,本质是“包含函数、变量和table的
独立Lua文件
”,通过特定规则导出接口,供其他脚本导入使用。其核心价值在于:避免全局变量污染、实现功能复用、简化大型项目维护。
模块的基本特征
- 物理形式:一个独立的
.lua
文件(如math_utils.lua
),文件名通常为模块名;- 内部结构:通过局部变量/函数定义实现内部逻辑,仅导出需公开的接口;
- 导出方式:通常返回一个table(包含公开函数/变量),作为模块的接口载体;
- 使用方式:通过
require
函数导入,类似其他语言的import
或include
。示例:一个简单的模块文件
string_utils.lua
-- 内部私有函数(未导出,仅模块内可用) local function trim_space(s) return s:gsub("^%s+", ""):gsub("%s+$", "") end -- 公开接口(放入table并返回) local StringUtils = { version = "1.0.0", -- 公开函数(调用私有函数) trim = function(s) return trim_space(s) end, -- 字符串长度计算(忽略空格) length_without_space = function(s) return #trim_space(s) end } return StringUtils -- 导出接口table
2️⃣ 模块操作:创建、导出与导入
模块的完整使用流程包括“创建模块文件→定义并导出接口→在其他脚本中导入使用”,核心是掌握
require
函数的用法与模块路径配置。
模块的创建与导出
创建模块需遵循“内部逻辑私有化、接口公开化”原则,常用导出方式有两种:
table导出法(推荐):将所有公开接口放入table并返回,结构清晰:
-- 模块文件:calc.lua local Calc = {} -- 接口容器 -- 公开函数:加法 function Calc.add(a, b) return a + b end -- 公开函数:乘法 function Calc.mul(a, b) return a * b end return Calc -- 导出接口
全局变量导出法(不推荐):直接定义全局函数/变量,易造成命名冲突:
-- 模块文件:bad_module.lua(不推荐) -- 直接定义全局函数(可能与其他模块冲突) function global_add(a, b) return a + b end -- 无return语句,依赖全局变量传递接口
模块的导入与使用
通过
require("模块名")
导入模块,返回模块导出的接口table,使用时需注意模块路径配置:
基本导入用法:
-- 导入calc模块(无需写.lua后缀) local calc = require("calc") -- 调用模块接口 print(calc.add(2, 3)) -- 输出:5 print(calc.mul(4, 5)) -- 输出:20
模块路径配置:
require
会在package.path
指定的路径中搜索模块文件,默认包含当前目录(./?.lua
)。若模块在子目录(如lib/
),需手动添加路径:-- 添加子目录lib到模块搜索路径 package.path = package.path .. ";./lib/?.lua" -- 导入lib目录下的logger模块 local logger = require("logger")
重复导入:
require
会缓存已导入的模块(存储在package.loaded
中),重复调用仅返回缓存结果。如需重新加载,需先清除缓存:package.loaded["calc"] = nil -- 清除calc模块缓存 local new_calc = require("calc") -- 重新导入
也可以使用dostring等函数
子文件夹模块导入
如果被导入的lua文件存放在子目录中,目录层级用
.
分割,例如:被导入文件路径为
lib/const.lua
local c = { X= 1, P = 3.1415926 } return c
则引入时应该按照下面的写法进行:
local c = require('lib.const') print(c.P)
执行结果如下所示:
3️⃣ 模块实战:依赖管理与项目结构
实际项目中,模块常存在依赖关系(如A模块依赖B模块),需合理设计目录结构并处理依赖加载顺序,确保模块正常工作。
模块依赖与加载顺序
若模块A依赖模块B,需保证B先被导入,Lua会自动处理已缓存的依赖模块:
-- 模块文件:advanced_calc.lua(依赖calc模块) local calc = require("calc") -- 先导入依赖 local AdvancedCalc = {} -- 基于calc.add实现的累加函数 function AdvancedCalc.sum(...) local total = 0 for _, v in ipairs({...}) do total = calc.add(total, v) -- 调用依赖模块的函数 end return total end return AdvancedCalc
使用时无需关心内部依赖顺序,直接导入最终模块即可:
local adv_calc = require("advanced_calc") -- 自动先加载calc print(adv_calc.sum(1, 2, 3, 4)) -- 输出:10
推荐项目结构示例
中小型项目建议采用以下模块目录结构,清晰区分功能模块与主程序:
project/ ├── main.lua # 主程序入口 ├── lib/ # 第三方模块目录 │ ├── json.lua # JSON处理模块 │ └── logger.lua # 日志模块 └── mymodule/ # 自定义模块目录 ├── calc.lua # 计算模块 └── string_utils.lua # 字符串工具模块
主程序
main.lua
中导入模块的示例:-- 配置自定义模块路径 package.path = package.path .. ";./mymodule/?.lua;./lib/?.lua"
-- 导入自定义模块
local calc = require("calc")
local str_util = require("string_utils")
-- 导入第三方模块
local logger = require("logger")
-- 业务逻辑
logger.info("计算结果:" .. calc.add(10, 20)) -- 输出日志:计算结果:30
🛬 文章小结
- 核心概念:Lua模块是包含接口的
.lua
文件,通过返回table导出公开函数/变量,解决代码复用与全局污染问题;- 关键操作:
- 创建模块用table封装接口并返回;
- 导入用
require
函数,末尾不带扩展名;- 多次导入,只会运行一次
- 目录层级用
.
分割;- 路径配置需修改
package.path
;- 实战要点:依赖模块需先导入,项目中建议按功能划分模块目录,通过
package.loaded
管理模块缓存;- 最佳实践:优先使用table导出法,避免全局变量;合理配置模块路径,保持项目结构清晰。
📖 参考资料
- Lua官方手册:Lua 5.3 Reference Manual - Modules
- Lua教程-进阶部分:https://www.bilibili.com/video/BV1WR4y1E7ud