0Pricing
Lua Academy · 课时

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 mock

package.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.lua

require、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 反馈 — 无需本地设置。

此课程中的所有课时

  1. require 函数
  2. 编写模块文件
  3. package.path 和 package.cpath
  4. 模块模式和最佳实践
← 返回 Lua Academy