鍍金池/ 教程/ Linux/ 常用 Lua 開發(fā)庫 3-模板渲染
Web 開發(fā)實(shí)戰(zhàn)2——商品詳情頁
流量復(fù)制 /AB 測試/協(xié)程
常用 Lua 開發(fā)庫 1-redis、mysql、http 客戶端
Lua 模塊開發(fā)
常用 Lua 開發(fā)庫 3-模板渲染
HTTP 服務(wù)
Nginx+Lua 開發(fā)入門
安裝 Nginx+Lua 開發(fā)環(huán)境
Redis/SSDB+Twemproxy 安裝與使用
JSON 庫、編碼轉(zhuǎn)換、字符串處理

常用 Lua 開發(fā)庫 3-模板渲染

動(dòng)態(tài) web 網(wǎng)頁開發(fā)是 Web 開發(fā)中一個(gè)常見的場景,比如像京東商品詳情頁,其頁面邏輯是非常復(fù)雜的,需要使用模板技術(shù)來實(shí)現(xiàn)。而 Lua 中也有許多模板引擎,如目前我在使用的 lua-resty-template,可以渲染很復(fù)雜的頁面,借助 LuaJIT 其性能也是可以接受的。

如果學(xué)習(xí)過 JavaEE 中的 servlet 和 JSP 的話,應(yīng)該知道 JSP 模板最終會(huì)被翻譯成Servlet 來執(zhí)行;而 lua-resty-template 模板引擎可以認(rèn)為是 JSP,其最終會(huì)被翻譯成 Lua 代碼,然后通過 ngx.print 輸出。

而 lua-resty-template 和大多數(shù)模板引擎是類似的,大體內(nèi)容有:

  • 模板位置:從哪里查找模板;
  • 變量輸出/轉(zhuǎn)義:變量值輸出;
  • 代碼片段:執(zhí)行代碼片段,完成如 if/else、for 等復(fù)雜邏輯,調(diào)用對(duì)象函數(shù)/方法;
  • 注釋:解釋代碼片段含義;
  • include:包含另一個(gè)模板片段;
  • 其他:lua-resty-template 還提供了不需要解析片段、簡單布局、可復(fù)用的代碼塊、宏指令等支持。

首先需要下載 lua-resty-template

Java 代碼

cd /usr/example/lualib/resty/  
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template.lua  
mkdir /usr/example/lualib/resty/html  
cd /usr/example/lualib/resty/html   
wget https://raw.githubusercontent.com/bungle/lua-resty-template/master/lib/resty/template/html.lua    

接下來就可以通過如下代碼片段引用了

Java 代碼

local template = require("resty.template")  

模板位置

我們需要告訴 lua-resty-template 去哪兒加載我們的模塊,此處可以通過 set 指令定義template_location、template_root 或者從 root 指令定義的位置加載。

如我們可以在 example.conf 配置文件的 server 部分定義

Java 代碼

\#first match ngx location  
set $template_location "/templates";  
\#then match root read file  
set $template_root "/usr/example/templates";  
也可以通過在server部分定義root指令  

Java 代碼

root /usr/example/templates;  

其順序是

Java 代碼

local function load_ngx(path)  
    local file, location = path, ngx_var.template_location  
    if file:sub(1)  == "/" then file = file:sub(2) end  
    if location and location ~= "" then  
        if location:sub(-1) == "/" then location = location:sub(1, -2) end  
        local res = ngx_capture(location .. '/' .. file)  
        if res.status == 200 then return res.body end  
    end  
    local root = ngx_var.template_root or ngx_var.document_root  
    if root:sub(-1) == "/" then root = root:sub(1, -2) end  
    return read_file(root .. "/" .. file) or path  
end   
  1. 通過 ngx.location.capture從template_location 查找,如果找到(狀態(tài)為為 200 )則使用該內(nèi)容作為模板;此種方式是一種動(dòng)態(tài)獲取模板方式;
  2. 如果定義了 template_root,則從該位置通過讀取文件的方式加載模板;
  3. 如果沒有定義 template_root,則默認(rèn)從 root 指令定義的 document_root 處加載模板。

此處建議首先 template_root,如果實(shí)在有問題再使用 template_location,盡量不要通過 root 指令定義的 document_root 加載,因?yàn)槠浔旧淼暮x不是給本模板引擎使用的。

接下來定義模板位置

Java 代碼

mkdir /usr/example/templates  
mkdir /usr/example/templates2  

example.conf 配置 server 部分

Java 代碼

\#first match ngx location  
set $template_location "/templates";  
\#then match root read file  
set $template_root "/usr/example/templates";  

location /templates {  
     internal;  
     alias /usr/example/templates2;  
}    

首先查找 /usr/example/template2,找不到會(huì)查找 /usr/example/templates。

然后創(chuàng)建兩個(gè)模板文件

Java 代碼

vim /usr/example/templates2/t1.html  

內(nèi)容為

Java 代碼

template2  

Java 代碼

vim /usr/example/templates/t1.html   

內(nèi)容為

Java 代碼

template1  

test_temlate_1.lua

Java 代碼

local template = require("resty.template")  
template.render("t1.html")  

example.conf 配置文件

Java 代碼

location /lua_template_1 {  
    default_type 'text/html';  
    lua_code_cache on;  
    content_by_lua_file /usr/example/lua/test_template_1.lua;  
}    

訪問如 http://192.168.1.2/lua_template_1 將看到 template2 輸出。然后 rm/usr/example/templates2/t1.html,reload nginx 將看到 template1 輸出。

接下來的測試我們會(huì)把模板文件都放到 /usr/example/templates 下。

API

使用模板引擎目的就是輸出響應(yīng)內(nèi)容;主要用法兩種:直接通過 ngx.print 輸出或者得到模板渲染之后的內(nèi)容按照想要的規(guī)則輸出。

test_template_2.lua

Java 代碼

local template = require("resty.template")  
--是否緩存解析后的模板,默認(rèn)true  
template.caching(true)  
--渲染模板需要的上下文(數(shù)據(jù))  
local context = {title = "title"}  
--渲染模板  
template.render("t1.html", context)  

ngx.say("<br/>")  
--編譯得到一個(gè)lua函數(shù)  
local func = template.compile("t1.html")  
--執(zhí)行函數(shù),得到渲染之后的內(nèi)容  
local content = func(context)  
--通過ngx API輸出  
ngx.say(content)    

常見用法即如下兩種方式:要么直接將模板內(nèi)容直接作為響應(yīng)輸出,要么得到渲染后的內(nèi)容然后按照想要的規(guī)則輸出。

examle.conf 配置文件

Java 代碼

location /lua_template_2 {  
    default_type 'text/html';  
    lua_code_cache on;  
    content_by_lua_file /usr/example/lua/test_template_2.lua;  
}  

使用示例

test_template_3.lua

Java 代碼

local template = require("resty.template")  

local context = {  
    title = "測試",  
    name = "張三",  
    description = "<script>alert(1);</script>",  
    age = 20,  
    hobby = {"電影", "音樂", "閱讀"},  
    score = {語文 = 90, 數(shù)學(xué) = 80, 英語 = 70},  
    score2 = {  
        {name = "語文", score = 90},  
        {name = "數(shù)學(xué)", score = 80},  
        {name = "英語", score = 70},  
    }  
}  

template.render("t3.html", context)    

請確認(rèn)文件編碼為 UTF-8;context 即我們渲染模板使用的數(shù)據(jù)。

模板文件 /usr/example/templates/t3.html

Java 代碼

{(header.html)}  
   <body>  
      {# 不轉(zhuǎn)義變量輸出 #}  
      姓名:{* string.upper(name) *}<br/>  
      {# 轉(zhuǎn)義變量輸出 #}  
      簡介:{{description}}<br/>  
      {# 可以做一些運(yùn)算 #}  
      年齡: {* age + 1 *}<br/>  
      {# 循環(huán)輸出 #}  
      愛好:  
      {% for i, v in ipairs(hobby) do %}  
         {% if i > 1 then %},{% end %}  
         {* v *}  
      {% end %}<br/>  

      成績:  
      {% local i = 1; %}  
      {% for k, v in pairs(score) do %}  
         {% if i > 1 then %},{% end %}  
         {* k *} = {* v *}  
         {% i = i + 1 %}  
      {% end %}<br/>  
      成績2:  
      {% for i = 1, #score2 do local t = score2[i] %}  
         {% if i > 1 then %},{% end %}  
          {* t.name *} = {* t.score *}  
      {% end %}<br/>  
      {# 中間內(nèi)容不解析 #}  
      {-raw-}{(file)}{-raw-}  
{(footer.html)}    
  • {(include_file)}:包含另一個(gè)模板文件;
  • { var }:變量輸出;
  • {{ var }}:變量轉(zhuǎn)義輸出;
  • {% code %}:代碼片段;
  • {# comment #}:注釋;
  • {-raw-}:中間的內(nèi)容不會(huì)解析,作為純文本輸出;

模板最終被轉(zhuǎn)換為 Lua 代碼進(jìn)行執(zhí)行,所以模板中可以執(zhí)行任意 Lua 代碼。

example.conf 配置文件

Java 代碼

location /lua_template_3 {  
    default_type 'text/html';  
    lua_code_cache on;  
    content_by_lua_file /usr/example/lua/test_template_3.lua;  
}    

訪問如 http://192.168.1.2/lua_template_3 進(jìn)行測試。

基本的模板引擎使用到此就介紹完了。