如何在Lua中构建一个独立的模块?

时间:2014-08-24 13:45:18

标签: module lua require

我正在为Love2D创建一个Lua库,其中包含相当多的内部子模块,类文件等。

我现在正在做的事情如下:

档案./libname/init.lua
lib.prefix = (...):match("(.-)[^%.]+$") .. "libname."
lib = {}

lib.class = require(lib.prefix .. "lib.class")
lib.types.Blah = require(lib.prefix .. "types.Blah")

return lib
档案./libname/types/Blah.lua
local Blah = lib.class()
...
return Blah

除了这里的东西是lib是一个全局的,如果我把它变成一个本地,我无法正确构建像Blah这样的子模块,因为它们不再能够访问lib

这显然是一个被剥离的示例,但我认为它很好地证明了我的问题 - 我想将lib表本地化,然后返回它,以便包含库就像{{1}当我需要模块本身时,而不是将整个事物导入全局范围。有可能吗?

2 个答案:

答案 0 :(得分:3)

我为这个概念做了一个指南。你可以在这里找到它:

http://kiki.to/blog/2014/03/30/a-guide-to-authoring-lua-modules/

要解决您在问题中提到的具体问题,我会使用3个文件:core.lua来分享状态,更改核心的“真实文件”以及init来绑定所有内容起来。

./libname/core.lua是定义lib的地方,作为本地栏。它没有定义lib.types。它使用其他文件可能想要使用的实用程序“设置”,例如设置前缀或实用程序class

local lib = {}

lib.prefix = (...):match("(.-)[^%.]+$") .. "libname."

lib.class = require(lib.prefix .. "lib.class")

return lib

“常规文件”(如./libname/types/Blah.lua)使用这些实用程序,但根本不修改lib:

local lib = require 'core' -- or libname.core or using the current_folder trick

local Blah = lib.class()
...
return Blah

init.lua将所有内容绑定在一起:

local lib = require 'core' -- or libname.core or current_folder trick

lib.types.Blah = require(lib.prefix .. "types.Blah")

return lib

评论中提到的“当前文件夹技巧”在这里:http://kiki.to/blog/2014/04/12/rule-5-beware-of-multiple-files/#the-current_folder-trick

答案 1 :(得分:1)

让我们采取相关的paragraph from the documentation

  

require(modname)

     

[...]

     

找到加载器后,require使用两个参数调用加载器:modname和一个额外的值,取决于它如何获取加载器。 (如果加载器来自文件,则此额外值是文件名。)如果加载器返回任何非零值,则需要将返回值分配给package.loaded[modname]。如果加载器未返回非零值且未向package.loaded[modname]分配任何值,则require将为此条目指定true。在任何情况下,require都会返回package.loaded[modname]的最终值。

因此,具有这种递归依赖性的模块的方法是:

  1. 获取模块名称modname(第一个未命名的var-arg参数)
  2. 加载基本初始化所需的模块
  3. 如果模块完全设置为2(递归调用)
  4. ,则返回
  5. package.loaded[modname]
  6. 下设置模块表
  7. 设置足够的脚手架来启动子模块
  8. require子模块
  9. 设置其余模块。
  10. 返回(由于步骤4,无需返回任何内容)。
  11. local _M, modname = {}, {...}[1]
    local sub = require(modname..".sub")
    if package.loaded[modname] then return end
    package.loaded[modname] = _M
    -- Populate _M with everything needed to set up more modules
    _M.X = require(modname..".X")
    --Do the rest of this modules setup and that's it
    --return
    

    这允许你根本不创建任何全局变量,正如现代模块所适用的那样。


    如果在设置主模块时不需要任何子模块,请考虑使用按需加载:

    setmetatable(_M, {__index =
      function(t, k)
        t[k] = require(modname.."."..k)
        return t[k]
      end})
    

    如果您的模块只能构建为子模块以供外部使用,您可以将其全部放在主模块中,并让它将相应的成员表作为子模块注册(参见步骤2)。 /> 在这种情况下,子模块加载器只会require主模块并且不返回任何内容。