[教程]使用额外的 .lua 文件⚓︎
如果您想在 main.lua 文件之外加载一个额外的 .lua 文件,您可以使用 “require” 或 “include” 函数。两者都有不同的目的。
require
⚓︎
require
是一个内置的 Lua 函数。在Lua程序中,使用 require
是将代码拆分为多个文件的传统方式。例如:
main.lua
⚓︎
1 2 3 |
|
foo.lua
⚓︎
1 2 3 4 5 6 7 |
|
require
的一个重要方面是,当使用它时,它会缓存结果。因此,当一个文件在代码中的两个不同位置被 require 时,它将在第一个要求时正常执行所有代码,然后在第二个要求时直接返回对模块的引用。(这种默认行为是有意义的,因为不需要反复执行相同的代码。)
使用文件目录的 require
⚓︎
与其他编程语言不同,在 Lua 中,一般使用句点作为路径分隔符。例如,如果您想在名为 "foo" 的子目录中导入名为 “bar.lua” 的文件,则可以使用以下 require
语句:
1 |
|
当然,你也可以使用 /
作为分隔符。
require
的命名空间问题⚓︎
与其他编程语言中的导入语句不同,require
函数不使用相对路径。相反,它是基于传递给函数的确切字符串。(每个mod目录都被添加到要查看的目录列表中。)
这会给在 require
的路径中有重叠的 MOD 带来问题。例如,假设有两个 MOD,mod1和mod2。两个 MOD 都有一个名为"foo.lua"的文件,并且两个 MOD 都使用一个local foo = require("foo")
的 require 语句。 MOD 1 将正常工作,但当 MOD 2 加载时,其 require
语句实际上将从 MOD 1 返回 "foo.lua" 文件。
为了解决这个问题, MOD 通常会将所有 Lua 文件放在与 MOD 名称匹配的目录中。例如, MOD 1 将创建一个名为 mod1
的目录,并有一个导入语句,如:local foo = require("mod1.foo")
这样,只要没有其他被称为 mod1
的 MOD,就永远不会发生冲突。
require
的 luamod
问题⚓︎
luamod
是一个控制台命令,它将重新加载一个 MOD。当你正在开发一个 MOD,并且你想立即测试你的更改而不返回主菜单时,这很有帮助。
不幸的是,require
的缓存机制会导致 luamod
控制台命令无法正常工作。如果模块内部的代码被更新,在使用 luamod
命令后,它将不会反映在游戏中,因为对该模块的引用已经被缓存。
include
⚓︎
为了解决命名空间问题和 luamod
问题,Kilburn在Repentance补丁v1.06.J818中添加了一个名为 include
的以撒特有的API函数。include
的工作方式与 require
基本相同,只是它永远不会缓存结果,导致每次都执行代码。(它也永远不会从其他人的 MOD 中获取文件,即使路径相同。)
共享变量⚓︎
include
仅设计给没有副作用的纯模块。换言之,如果在具有模块级状态变量的模块上使用 include
,则它们将被实例化N次,每个 include
实例化一次。显然,这真的很糟糕,因为文件之间的内部状态将变得不同步。
因此,如果您具有模块级状态或需要在文件之间共享变量,则不能使用 include
,而必须使用 require
。
解决 require 问题⚓︎
如果您需要使用 require
而不是 include
,建议您将所有 Lua 代码都放在名称空间目录中,如前所述。如果你还想拥有 luamod”
功能,你可以启用 “--luadebug” 启动选项,然后用以下内容修改 require
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
解决 require 问题的替代方法⚓︎
同样值得注意的是,如果您使用 IsaacScript framework 和 TypeScript 编写 MOD,则上述 require
问题不存在。这是因为 transpiler 会自动将所有代码组合到一个 “main.lua” 文件中。这意味着,您不必在使用 include
和 require
之间左右为难,也不必担心状态问题,也不需要对 require
函数进行猴子补丁——您只需编写有效的代码即可。