Lua中的模块和使用

简介

从Lua5.1版本开始,就对模块/包添加了新的支持,可是使用require函数和package函数来加载模块,使用table模拟module来定义模块。
函数require用于加载模块,module用于创建模块。

传统模式下的模块机制module

1.什么是module

对开发来说,使用module可以有效分隔代码,实现代码共享,便于代码管理。
对于用户来说,一个module相当于一个Xnix中的共享库so或者windows中的动态库dll。

2.如何编写module

lua是通过table来实现模块的,典型的写法如下。

定义一个lua模块文件 – moduleA.lua

-- moduleA.lua
-- 通常是加local的,加了local是局部变量,需要return一下。
-- 如果不加,则M默认注册到_G中,require后,即使不return也可以直接使用M。

local M = {}    -- 通过table来实现模块

M.work = function(...)
    print("function working")
    for i, v in ipairs{...} do
          print(i, v)
     end
     -- do some job.
end

return M

这里定义了一个具有变长参数的函数–其函数参数个数可变。参数的定义形式为...,在内部访问时,也使用{...},和使用table的方式一样。


3.使用module

要使用定义好的module,使用require函数,加载定义的module,然后就可以使用。

-- test.lua

local m = require "moduleA"

m.work('a','b',1,2,3)

module加载位置

要加载一个模块,就必须的知道这个模块在哪里。知道了这个模块在哪里以后,才能进行正确的加载。当我们写下require “mod”这样的代码以后,Lua是如何找这个mod的呢?

在搜索一个文件时,在windows上,很多都是根据windows的环境变量path来搜索,而require所使用的路径与传统的路径不同,require采用的路径是一连串的模式,其中每项都是一种将模块名转换为文件名的方式。require会用模块名来替换每个“?”,然后根据替换的结果来检查是否存在这样一个文件,如果不存在,就会尝试下一项。路径中的每一项都是以分号隔开。而且lua模块包括lua模块,以及c实现的模块。其对于require用于搜索的Lua文件的路径存放在变量package.path和package.cpath中。我们可以在输出看看。

检查一下lua系统中的环境,执行命令:

$ lua
Lua 5.2.4  Copyright (C) 1994-2015 Lua.org, PUC-Rio
stdin:1: unexpected symbol near char(228)
> print(package.path)
/usr/local/share/lua/5.2/?.lua;/usr/local/share/lua/5.2/?/init.lua;/usr/local/lib/lua/5.2/?.lua;/usr/local/lib/lua/5.2/?/init.lua;./?.lua
> print(package.cpath)
/usr/local/lib/lua/5.2/?.so;/usr/local/lib/lua/5.2/loadall.so;./?.so

可以再看看各种函数和对象的类型

> print(print)
function: 0x104cb304d
> print(require)
function: 0x7fdc3b403db0
> print(package)
table: 0x7fdc3b403740
> print(_G)
table: 0x7fdc3b4028d0
> print(module)
function: 0x7fdc3b403d60
> 

require机制

1.require实现原理:

function require(name)

    if not packge.loaded[name] then          ---- 避免重复加载

        local loader = findloader(name)      ---- 如果是so,就以`loadlib`方式加载文件,如果是lua文件,就以`loadfile`方式加载文件。

        if loader == nil then
            error("unable to load module " .. name)
        end

        package.loaded[name] = true 
        -- 将模块标记为以加载,我们有时候会看到require返回true的现象,是由于被调用的模块,没有显示的执行package.loaded[modname] = M
        -- 或者给出return M这样的返回值。

        local res = loader(name)        
        -- require会以name作为入参来执行该文件,如果有返回结果,就将返回结果保存在package.loaded[name]中,
        -- 如果没有返回结果,就直接返回package.loaded[name]。如果我们在被调用的文件中直接写明return 1。
        -- 则调用者的require的返回结果就是1。但是只要我们显示的在require文件中写明了_G[modname] = M,
        -- 我们仍然可以在require之后,直接使用M作为名字来调用,是由于将M加入到了_G中。

        if res ~= nil then
            package.loaded[name] = res
        end

    end

    return package.loaded[name]
end

2.require解析:

  • 传参: require会将模块名作为参数传递给模块

  • 返回值:如果一个模块没有返回值的话,require就会返回package.loaded[modulename]作为返回值。

3. package.loaded是系统使用的table

require会将返回值存储到package.loaded–一个table– 中;如果加载器没有返回值,require就会返回table package.loaded中的值。可以看到,我们上面的代码中,模块没有返回值,而是直接将模块名赋值给table package.loaded了。这说明package.loaded这个table中保存了已经加载的所有模块。

现在我们就可以看看require到底是如何加载的呢?

1.先判断package.loaded这个table中有没有对应模块的信息;
2.如果有,就直接返回对应的模块,不再进行第二次加载;
3.如果没有,就加载,返回加载后的模块。

环境

lua用_G一张表保存了全局数据(变量,函数和表等)。

我们在lua中定义一个module,如果不加local,则它是一个注册在全局下的表。我们通过加local避免了它在污染全局表空间,只在本文件生效。如果我们没有将其注册到_G下,在其他文件是无法直接通过他的原始名字来访问的。这里有一个不便利的地方,每个函数前面都要带MM的下的函数相互访问也要带M头。

解决方法:通过setfenv

local modname = ...

local M = {}

_G[modname] = M

package.loaded[modname] = M

setfenv(1, M)

后续的函数直接定义名字,因为他们的环境空间已经由_G改为了M

如果要使用全局函数,则可以本地额外增加一条local _G = _G或者setmetatable(M, {__index = G})

更好的方法是在setfenv之前将需要的函数都保存起来,local sqrt = math.sqrt

定义module – 使用module函数

在定义一个模块时,前面的几句代码都是一样的,就分为以下几步:

1.从require传入的参数中获取模块名;
2.建立一个空table;
3.在全局环境_G中添加模块名对应的字段,将空table赋值给这个字段;
4.在已经加载table中设置该模块;
5.设置环境变量。

就是这几步,在每一个模块的定义之前都需要加上,有点麻烦,在Lua5.1中提供了一个新函数module,它替代我们完成以上这些步骤完成的功能。

在编写一个模块时,可以直接用以下代码来取代前面的设置代码:

local moduleName = ...

local M = {}            -- 局部的变量
_G[moduleName] = M      -- 将这个局部变量最终赋值给模块名

package.loaded[moduleName] = M

local sqrt = math.sqrt  -- 在我们自己的模块中需要用到math.sqrt这个函数,所以就先保存下来
local io = io           -- 需要用到io库,也保存下来
setfenv(1, M)           -- 设置完成以后,就不能再使用_G table中的内容了

等同于

module(modname)。

默认的情况下,module不提供外部的访问的,也就是说,你无法访问前一个环境了。如果要访问外部变量,两种方法:

  • 1.在声明module之前,local 变量 = 外部变量

  • 2.使用module(modname, package.seeall), 等价于setmetatable(M, __index = _G)

在使用module时是这样解决的:

module(..., package.seeall)

其功能就好比之前的功能再加上了setmetatable(M, {__index = _G})。有了这一句代码,基本上就可以说万事不愁了。

使用module函数实现模块化

网上一个小示例:

先定义一个module:

mypack.lua

--mypack.lua
module(..., package.seeall) --定义module
ver = "0.1 alpha"
function aFunInMyPack()
    print("Hello!")
end
_G.aFuncFromMyPack = aFunInMyPack

测试代码test.lua:

--testP.lua:
pack = require "mypack" --导入module
print(ver or "No ver defined!")
print(pack.ver)
print(aFunInMyPack or "No aFunInMyPack defined!")
pack.aFunInMyPack()
print(aFuncFromMyPack or "No aFuncFromMyPack defined!")
aFuncFromMyPack()

执行结果:

$lua test.lua 
No ver defined!
0.1 alpha
No aFunInMyPack defined!
Hello!
function: 0x7fe1e1501390
Hello!

目录层次

如果要更好地组织代码,可以把不同的模块放到不同的目录层次。
比如

ls -l -R
total 8
drwxr-xr-x  3 david  staff   96  3 23 12:07 sub
-rw-r--r--@ 1 david  staff  288  3 23 12:50 test.lua

./sub:
total 8
-rw-r--r--@ 1 david  staff  174  3 23 12:11 mypack.lua

在工作执行目录下,包括了test.lua,以及sub目录,其下放置了mypack.lua – 对应mypack模块。

则test.lua中require时,需要包括模块目录形式,require "sub.mypack"
注意这里使用的是.,而不是\

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Nginx配置Lua模块,你需要遵循以下步骤: 1. 在安装Nginx之前,请确保你已经安装了LuaJIT或者Lua的开发库。你可以通过在终端运行以下命令来安装它们: 对于Ubuntu/Debian系统: ``` sudo apt-get install libluajit-5.1-dev ``` 对于CentOS/RHEL系统: ``` sudo yum install lua-devel ``` 2. 下载Nginx的源码包,并解压缩它。 3. 在解压缩后的目录,运行以下命令来配置编译选项,其`--prefix`是安装目录的路径: ``` ./configure --prefix=/path/to/install --add-module=/path/to/nginx-lua-module ``` 请将`/path/to/nginx-lua-module`替换为你实际安装Lua模块的路径。 4. 运行以下命令编译和安装Nginx: ``` make sudo make install ``` 5. 配置Nginx以使用Lua模块。在Nginx的配置文件(通常是`nginx.conf`)添加以下内容: ``` http { lua_package_path "/path/to/lua-scripts/?.lua;;"; lua_package_cpath "/path/to/lua-modules/?.so;;"; server { ... location / { ... content_by_lua_file /path/to/lua-script.lua; } } } ``` 请将`/path/to/lua-scripts`替换为你实际存放Lua脚本的路径,将`/path/to/lua-modules`替换为你实际存放Lua模块的路径,将`/path/to/lua-script.lua`替换为你实际的Lua脚本文件路径。 6. 保存并关闭配置文件后,重新启动Nginx服务。你现在应该可以在浏览器访问配置的Lua脚本了。 这些步骤涵盖了在Nginx配置Lua模块的基本过程。请根据你自己的需求进行相应的调整和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值