首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >在Lua中循环Require是如何处理的?

在Lua中循环Require是如何处理的?

作者头像
keyle
发布2025-08-08 14:33:29
发布2025-08-08 14:33:29
21300
代码可运行
举报
文章被收录于专栏:礼拜八不工作礼拜八不工作
运行总次数:0
代码可运行

在 Lua 中,当多个脚本文件循环 require 时(例如 A 依赖 B,B 又依赖 A),最后 require 的值为 true 是由于 Lua 的 模块加载机制避免无限循环 的设计导致的:

  1. 模块加载状态跟踪:Lua 使用 package.loaded 表跟踪已加载的模块。
  2. 占位符机制:当开始加载模块 A 时,会先在 package.loaded 中设置 A = true(临时占位符)。
  3. 循环检测:若在加载 A 的过程中遇到 require B,而 B 又尝试 require A
    • 此时 package.loaded[A] 已存在(值为 true)。
    • Lua 会直接返回这个占位符值 true,避免无限循环。
  4. 最终值替换:当 A 完全加载后,其返回值会替换占位符(但循环依赖的模块已获取了占位值 true)。

在lua5.1中,出现循环require会直接报错(这边我们不讨论在5.1下的情况),如果报错是非常容易排查的,如下图:

lua5dot1error
lua5dot1error
代码案例演示

假设有两个文件互相依赖:

test_script
test_script
执行结果:
代码语言:javascript
代码运行次数:0
运行
复制
Start loading A
Start loading B
In B, a = true      <-- 循环 require 导致值为 true
In A, b = Module B  <-- B 正常加载完成
In main, a = Module A
关键点说明
  1. 加载流程:
    • main.lua 执行 require "a",开始加载 A。
    • A 执行 require "b",开始加载 B。
    • B 执行 require "a",此时 A 正在加载中(package.loaded[a] = true),直接返回 true
  2. 值的变化:
    • B 中的 a 获取到占位符 true
    • A 加载完成后,package.loaded["a"] 被替换为 "Module A"
    • 但 B 中已获取的 a 值不会更新(仍是 true)。

Lua 源码分析(以 Lua 5.4 为例)

关键函数在 loadlib.c 中的 ll_require 函数:

核心逻辑
代码语言:javascript
代码运行次数:0
运行
复制
static int ll_require (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);

  // 1. 检查模块是否已加载
  lua_settop(L, 1);
  lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
  if (lua_getfield(L, 2, name) != LUA_TNIL) { // 已存在
    if (lua_toboolean(L, -1) == 0) // 值为 false 表示加载失败
      luaL_error(L, "module '%s' not found", name);
    return 1;
  }

  // 2. 设置临时占位符 true
  lua_pushboolean(L, 1);
  lua_setfield(L, 2, name); // package.loaded[name] = true

  // 3. 加载模块代码...
  // ...(此处调用加载器执行文件内容)

  // 4. 如果模块未返回值,则保持 true 不变
  if (lua_getfield(L, 2, name) == LUA_TNIL) {
    lua_pushboolean(L, 1);
    lua_pushvalue(L, -1);
    lua_setfield(L, 2, name); // 无返回值时设为 true
  }
  return 1;
}
关键步骤:
  1. 检查 package.loaded:若模块已存在,直接返回其值。
  2. 设置占位符:在加载前设置 package.loaded[name] = true,标记模块正在加载。
  3. 处理循环依赖:当依赖模块尝试 require 当前模块时,直接返回占位符 true
  4. 替换最终值:模块加载完成后,用返回值替换占位符(若未返回值,则保持 true)。

解决方案:避免循环依赖

重构代码

:解耦模块间的双向依赖。

延迟加载

:在需要时再 require(例如在函数内部调用)。

代码语言:javascript
代码运行次数:0
运行
复制
   -- b.lua 修复版
   local a
   function get_a()
     if not a then a = require "a" end
     return a
   end

显式传递依赖

:通过参数传递避免 require

最佳实践:模块设计应遵循 单向依赖 原则,避免循环 require。若无法避免,需明确处理占位值 true 的情况。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 礼拜八不工作 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码案例演示
    • 执行结果:
  • 关键点说明
  • Lua 源码分析(以 Lua 5.4 为例)
    • 核心逻辑
    • 关键步骤:
  • 解决方案:避免循环依赖
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档