【Lua学习笔记7】Lua中的模块加载

2,845 阅读3分钟

require关键字

lua中使用require来加载对应的模块。

基本用法:

比如我有个一个文件test.lua,内容如下:

test = {}

test.fun1 = function() return 1 end

return test;

然后在main.lua中使用require引入:

require('test')
print(test.fun1()) --> 1

加载过程

跟node加载模块一样,lua中的模块加载过程也需要一个查找过程。在使用require加载lua模块的时候,lua首先会查看package.loaded表中是否存在该模块,如果存在,说明该模块之前已经加载过了,那么久直接返回这个表中的对应模块。

其实就是模块的全局缓存的效果,保证每个模块只需加载一次就够了。

如果package.loaded表中没有该模块,则会先针对这个模块寻找一个loader,这个loader工作你懂得,就是用来加载这个模块的。

所以现在的流程就是,

    1. require(moduleName)
    1. 看下package.loaded[moduleName]有没有值,没有走第三步
    1. 看下package.preload[moduleName]有没有值,有就返回,没有走第四步
    1. 使用packege.path中指定的路径去搜索loader,如果失败走第五步
    1. 使用packge.cpath中指定的路径去搜索C loader,如果还是失败了,则走第六步
    1. 使用package.searchers中配置的loader来加载模块。

如果以上的流程中查找到了模块的loader,则require会调用这个加载程序,并且将模块名称和一个额外的参数传递给该loader(如果这个loader来自一个文件,则该参数的值为文件名)。如果loader没有给package.loaded添加任何值而且也没有返回任何的非零值,则讲package.loaded[moduleName]设置为true。然后require会返回package.loaded[moduleName]给到调用方。

在上面的加载过程中,如果出现了失败或者错误,require就会抛出一个错误。

package.preload

用来存储一些特殊的模块,我的理解就是用来存储一些自定义的loader的。

package.path

这个配置用来告诉lua去哪里查找模块的loader。 在启动时,Lua使用环境变量LUA_PATH_5_3或环境变量LUA_PATH的值或使用luaconf.h中定义的默认路径初始化此变量(如果未定义这些环境变量)。任何 ”;;”在环境变量的值中由默认路径替换。

package.cpath

跟path差不多,主要用来加载C语言的库,这个就不说了。

package.searchers

package.searchers是lua用来控制如何加载以及去哪里加载对应模块的表。这个表里面的每一个属性都是一个查找函数,lua会按升序排序,然后根据这个顺序来调用查找函数,将require函数传递过来的参数给到查找函数,然后去执行对应的查找。

lua在初始化package.searchers表的时候,会添加以下4个查找函数:

    1. 第一个查找函数只在package.preload表中查找loader
    1. 第二个查找函数,按package.path中的配置路径来查找loader,匹配过程用package.searchpath中的配置说明来匹配
    1. 第三个是加载C语言库的,跟path的一样
    1. 第四个也是用来加载C语言库的,就不说了,有需要的查看这里的官方文档

package.searchpath

package.searchpath (name, path [, sep [, rep]])
  • name: 搜索的名称
  • path: 指定的搜索路径
  • sep:名称中的分隔符,默认是一个点(.)
  • rep: 在路径中的占位符,最终会被实际的名称给替换,默认为?

举个例子: 如果path中配置了下面这个路径:

"./?.lua;./?.lc;/usr/local/?/init.lua"

然后需要查找一个foo.a模块的话,则会去寻找这些路径的文件:

./foo/a.lua
./foo/a.lc
/usr/local/foo/a/init.lua

返回第一个查找的结果,如果没有找到,则返回nil和错误信息(尝试打开的所有文件)。