require 函数
使用 require 加载外部模块,并理解模块缓存机制。
require 函数 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。
require 的工作原理
require(modname) 会加载并返回一个模块。它会在 package.path 中搜索 .lua 文件,并在 package.cpath 中搜索 C 库。结果会缓存到 package.loaded[modname] 中——后续调用会返回缓存值,而不会再次执行该文件。
-- First require: loads and executes math_utils.lua
local math_utils = require("math_utils")
-- Second require: returns cached value (file not re-run)
local math_utils2 = require("math_utils")
print(math_utils == math_utils2) -- true (same object)
print(package.loaded["math_utils"] == math_utils) -- true模块返回值
模块文件应返回一个值——通常是由多个函数组成的表。这个返回值就是 require 返回给调用方的内容。如果文件没有返回任何内容(或返回 true),require 就会返回该结果。
-- mymodule.lua:
-- local M = {}
-- function M.hello() print("Hello from module!") end
-- return M
-- In main script:
local m = require("mymodule")
m.hello() -- Hello from module!
-- The module table is the interface在 package.loaded 中缓存
package.loaded 是一个将模块名称映射到已加载值的表。您可以手动设置某个条目,以预先加载模拟对象;也可以将其设为 nil,强制重新加载(模块文件会在下一次 require 时重新执行)。
-- Force reload by clearing cache
package.loaded["mymodule"] = nil
local fresh = require("mymodule") -- re-executes the file
-- Preload a mock for testing
package.loaded["db"] = {
query = function() return {} end,
close = function() end,
}
local db = require("db") -- gets the mockpackage.path 格式
package.path 是由分号分隔的模式列表。占位符 ? 会替换为模块名称(其中的点会替换为路径分隔符)。您可以将目录添加到 package.path 的开头,以增加搜索目录。
print(package.path)
-- typically: ./?.lua;./?.luac;/usr/share/lua/5.4/?.lua;...
-- Add local lib directory
package.path = "./lib/?.lua;" .. package.path
-- Now require("utils") will look in ./lib/utils.luarequire、dofile 与 loadfile
require:会缓存,并搜索 package.path。dofile(path):立即执行文件,不进行缓存,接收直接路径。loadfile(path):编译但不执行,返回一个函数。模块应使用 require;一次性脚本应使用 dofile。
-- dofile: no caching, direct path
dofile("./scripts/setup.lua")
-- loadfile: compile only, returns function
local fn, err = loadfile("./config.lua")
if fn then
local result = fn() -- execute when ready
end
-- require: best for modules
local json = require("json")使用点号让 require 访问目录
在模块名称中使用点号,可以引用子目录中的文件。require("utils.string") 会映射到 utils/string.lua(路径模式中包含 ?)。多文件库就是这样组织的。
-- Directory structure:
-- lib/
-- utils/
-- string.lua
-- table.lua
-- init.lua
package.path = "./lib/?.lua;" .. package.path
local strUtils = require("utils.string") -- loads lib/utils/string.lua
local tblUtils = require("utils.table") -- loads lib/utils/table.lua模块初始化副作用
模块文件在第一次 require 时执行一次。顶层的任何代码(函数外部的代码)都会在 require 时运行。这对于一次性初始化很有用,但请注意:网络连接或文件操作等副作用会在导入时执行。
-- counter.lua:
-- local count = 0 -- initialized once on first require
-- local M = {}
-- function M.increment() count = count + 1 end
-- function M.get() return count end
-- return M
local c = require("counter")
c.increment()
c.increment()
print(c.get()) -- 2
local c2 = require("counter")
print(c2.get()) -- 2 (same instance, cached)require 中的错误处理
如果模块文件存在语法错误,或在加载过程中抛出错误,require 就会抛出错误。错误消息包含模块名称以及文件内部产生的错误。请使用 pcall,以便优雅地处理模块加载失败。
local ok, mod = pcall(require, "possibly_missing")
if not ok then
print("Module not available:", mod)
-- fall back to alternative or default implementation
mod = {feature = function() return "fallback" end}
end
print(mod.feature())package.preload
package.preload 是一个表,您可以按模块名称在其中注册加载器函数。当调用 require("name") 时,Lua 会先检查 preload。这样,您无需文件即可注册模块——这对嵌入式 Lua 或测试很有用。
package.preload["mymath"] = function()
return {
double = function(n) return n * 2 end,
triple = function(n) return n * 3 end,
}
end
local m = require("mymath")
print(m.double(5)) -- 10
print(m.triple(5)) -- 15循环 require
循环依赖(A 要求 B,B 要求 A)会得到部分处理:Lua 会在执行模块前,将 true 占位值存入 package.loaded。如果 B 尝试使用尚未完成加载的 A 的表,可能会得到不完整的结果。模块设计中应避免循环依赖。
-- a.lua: local b = require("b"); local M = {}; M.name = "A"; return M
-- b.lua: local a = require("a"); print("a.name:", a.name); return {}
-- When a.lua is loaded:
-- 1. a starts executing
-- 2. requires b -> b starts executing
-- 3. b requires a -> gets partial a (still loading!)
-- 4. a.name may be nil at step 3
print("Avoid circular requires in module design")快速检查
第二次调用 require("mod") 时会发生什么?
回顾:require
总结:
require(name)只加载一次,并将结果缓存到package.loaded中- 模块文件会返回其公共接口(通常是一个表)
- 清除
package.loaded[name]可强制重新加载 - 将路径添加到
package.path,以使用自定义搜索目录 - 使用
package.preload创建内存中的模块 - 避免循环依赖
常见问题解答
「require 函数」课时是免费的吗?
是的 — 「require 函数」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。
「require 函数」这节课中我会学到什么?
使用 require 加载外部模块,并理解模块缓存机制。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Lua Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「require 函数」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Lua Academy 课中编写并运行代码吗?
能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。