一個兼容Liquid語法的模板引擎
TinyLiquid是一個基于**JavaScript**,兼容**Liquid**模板語法的的模板引擎。其具有以下特點:
-
強大靈活的語法:在模板當中,你可以使用諸如條件判斷、循環、賦值、函數調用等語法標記來進行控制
-
渲染速度快:目前僅做了簡單的測試,其速度為ejs的4倍以上 性能測試結果
-
異步渲染模式:可解決JavaScript程序中異步獲取多個數據時,難以控制的問題
-
可運行于瀏覽器端及Node.js環境下
下載 & 安裝
目前最新版為**v0.0.9**,可通過以下方式來獲取TinyLiquid模塊:
-
通過NPM安裝:**npm install tinyliquid**
-
通過git下載:**git clone git://github.com/leizongmin/tinyliquid.git**
-
在**瀏覽器**端使用:
<script src="https://raw.github.com/leizongmin/tinyliquid/master/build/target/tinyliquid.min.js"></script>
-
在**Express 3.x**中使用:模塊express-liquid
模板語法
輸出變量 {{name}}
例:
你好, {{ name }}
你好, {{ name | escape }} // 對變量值進行轉義
函數調用(Filters) {{value|method}}
例:
大寫:{{ name | upcase }}
現在的時間是:{{ "now" | date: "%H時%M分%S秒" }}
嵌套:{{ name | upcase | split: '-' }}
條件判斷 {%if condition%}...{%else%}...{%endif%}
例:
{% if name == "老雷" %} {{ name }},你好 {% endif %}
{% unless name contains "雷" %} 你不是老雷! {% endunless %}
{% if you.age < 18 %}
你還未成年吶~~
{% else %}
歡迎進入...
{% endif %}
說明:條件判斷操作符可支持等于(==)、不等于(!=或<>)、大于(>)、小于(<)、大于或等于(>=)、小于或等于(<=)、包含字符串(contains)
循環 {%for item in array%}...{%endfor%} {%tablerow item in array%}...{%endtablerow%}
例:
{% for item in array %}
{{ item }}
{% endfor %}
{% for item in array offset:2 limit:3 %}
{{ item }}
{% endfor %}
{% tablerow item in array cols:4 %}
{{ item }}
{% endtablerow %}
變量賦值 {%assign name = "value"%}
例:
{% assign name = "value" %}
{% assign name = value | upcase %}
保存局部輸出 {%capture name%}...{%endcapture%}
例:
{% capture name %}
{% for item in array %}
{% item %}
{% endfor %}
{% endcapture %}
array循環的輸出被保存到name當中了,通過`{{ name }}`可輸出該結果
常量循環 {%cycle 'one','two','three'%}
例:
{% cycle 'one', 'two', 'three' %},
{% cycle 'one', 'two', 'three' %},
{% cycle 'one', 'two', 'three' %},
{% cycle 'one', 'two', 'three' %}
將輸出:one,two,three,one
包含文件 {%include "filename"%}
例:
{% include "header" %}
{% include "header" with data %}
不解析部分 {%raw%}...{%endraw%}
例:
{% raw %}{{ 5 | plus: 6 }}{% endraw %} is equal to {{ 5 | plus: 6 }}.
將輸出:{{ 5 | plus: 6 }} is equal to 11.
注釋部分 {%comment%}...{%endcomment%}
例:
Hello world. {% comment %} Now this is a single-line comment {% endcomment %} <br />
Hello world,
I think I'm gonna be happy today. {% comment %} Now this is a
multi-line comment that should be ignored too,
just like the single-line comment {% endcomment %}
將輸出:
Hello world.
Hello world, I think I'm gonna be happy today.
詳細語法說明可參考這里:
http://wiki.shopify.com/Liquid (英文)
https://wiki.shopqi.com/liquid (中文)
開始使用
載入模塊
在Node.js環境下:
var tinyliquid = require('tinyliquid');
在瀏覽器環境下:(在瀏覽器環境下,通過全局變量**TinyLiquid**來操作)
<!-- 該文件可在源碼里面的build/target目錄獲得 -->
<script src="tinyliquid.min.js"></script>
<script>
TinyLiquid.render('{{a}}', {a: 123});
</script>
編譯模板 compile(text, [options])
var tinyliquid = require('tinyliquid');
// 普通方式
var render = tinyliquid.compile('模板內容 {{ name }}');
// 如果模板中使用到了**include**標簽,則需要指定files選項
var options = {
files: {
'filename': 'content'
}
};
var render = tinyliquid.compile('模板內容 {{ name }}', options);
// 默認情況下,compile()返回的是封裝后的代碼,如果需要返回原始的js代碼,
// 可指定original選項
options.original = true;
var render = tinyliquid.compile('模板內容 {{ name }}', options);
高級編譯 compileAll(files, [options])
var tinyliquid = require('tinyliquid');
var files = {
'index': fs.readFileSync('index.html', 'utf8'),
'header': fs.readFileSync('header.html', 'utf8'),
'bottom': fs.readFileSync('bottom.html', 'utf8'),
};
var options= { original: true};
var render = tinyliquid.compileAll(files, options);
// 返回 {index: [Function], header: [Function], bottom: [Function]}
渲染 render(data, [filters]);
var tinyliquid = require('tinyliquid');
var render = tinyliquid.compile('模板內容 {{ name }}');
// 普通方式
var text = render(data);
// 自定義filters方式
var filters = {
upcase: function (s) {
return String(s).toUpperCase();
}
};
var text = render(data, filters);
// 如果在編譯模板的時候指定了選項original=true,則渲染時必須指定filters參數,
// 可以為 tinyliquid.filters ,否則,模板中將無法使用filters功能
高級渲染 advRender(render, models, options, callback)
var tinyliquid = require('tinyliquid');
var render = tinyliquid.compile('模板內容 {{ name }}');
// 定義models
var models = {
name: function (env, callback) {
setTimeout(function () {
callback(null, '你好');
}, 100);
}
};
// 選項
var options = {
parallel: false, // 可選,true 并行方式獲取,默認為false
filters: tinyliquid.filters, // 可選,自定義函數
env: {}, // 可選,環境變量,即models函數中的第一個參數
};
tinyliquid.advRender(render, models, options, function (err, text) {
if (err)
console.error(err);
else
console.log(text);
});
自定義函數 (filters)
var tinyliquid = require('tinyliquid');
var render = tinyliquid.compile('{{ "hello, world!" | my_filter }}');
// 編譯出來的函數中,第二個參數可用來指定自定義的filters集合
var filters = {};
// 先復制原來的filters,否則傳入自定義的filters后,將不能使用舊的
for (var i in tinyliquid.filters) {
filters[i] = tinyliquid.filters[i];
}
filters.my_filter = function (str) {
return String(str).replace('world', 'abc');
};
console.log(render({}, filters)); // 輸出 hello, abc!
自定義標簽 (tag)
var tinyliquid = require('tinyliquid');
// 自定義標簽
var tags = {};
// words 當前標簽中用空格隔開的單詞數組,除第一個之外
// line 當前標簽的文本,即 {% %} 里面的內容
// context 環境變量
// methods 為編譯時提供相應的方法,其中包含:
// loopNotMatch 返回標簽不匹配出錯信息
// syntaxError 返回語法錯誤出錯信息
// unknownTag 返回不可識別的標簽出錯信息
// localsWrap 取得渲染模板時的內部表示變量名稱
// printString 返回輸出字符串的js代碼
// printLocals 返回輸出變量的js代碼
// 自定義標簽函數應該返回該標簽所生成的js代碼,
// 若返回null將輸出一個syntaxError()出錯信息
tags.my_tag = function (words, line, context, methods) {
// 在本例中返回以下js代碼:
// $_buf += 'hello, ';\n
// $_buf += locals.name;\n
return methods.printString('hello, ') + methods.printLocals(words[0]);
};
var render = tinyliquid.compile('{% my_tag name %}', {tags: tags});
console.log(render({name: 'world'})); // 輸出: hello, world
使用建議
TinyLiquid沒有內置的緩存機制,因此在使用時,如果需要多次調用某個模板來進行渲染,需要 手動緩存編譯出來的渲染函數。
異步渲染模式
在Node.js編程中,最令人頭疼的是異步獲取數據的層層嵌套,還有錯綜復雜的回調,而 TinyLiquid的自動獲取數據功能可以解決該問題。以下為使用不同方式異步獲取數據的例子:
-
普通獲取數據方式 https://github.com/leizongmin/tinyliquid/blob/master/example/different_ways/normal.js
-
使用Jscex獲取數據方式 https://github.com/leizongmin/tinyliquid/blob/master/example/different_ways/jscex.js
-
使用EventProxy獲取數據方式 https://github.com/leizongmin/tinyliquid/blob/master/example/different_ways/eventproxy.js
-
使用TinyLiquid內置的advRender()自動獲取數據方式 https://github.com/leizongmin/tinyliquid/blob/master/example/different_ways/advrender.js
使用TinyLiquid的好處
-
避免程序的多層嵌套
-
不需要安裝更多的模塊
-
TinyLiquid僅在模板需要用到該數據時,才會執行獲取數據操作,因此你可以定義一個公共的獲取數據接口,當渲染不同的頁面時,TinyLiquid會根據需要獲取相應的數據,而不會去獲取模板中不需要的數據。
-
編寫更少的代碼
內置函數 filters
內置支持的函數詳見 lib/filters.js
標簽相關
-
{{'url' | img_tag: 'alt'}} 生成
<img>
標簽 -
{{'url' | script_tag}} 生成
<script>
標簽 -
{{'url' | stylesheet_tag: 'media'}} 生成
<link>
CSS標簽 -
{{'link' | link_to: 'url', 'title'}} 生成
<a>
標簽
算數運算相關
-
{{123 | plus: 456}} 相加
-
{{123 | minus: 456}} 相減
-
{{123 | times: 456}} 相乘
-
{{123 | divided_by: 456}} 相除
-
{{1.23 | round: 2}} 四舍五入,保留小數點后2位
-
{{1.23 | integer}} 取整
-
{{1 | random: 2}} 返回1<=N<2的隨機數
-
{{N | pluralize: 'item', 'items'}} 如果N大于1則返回items,否則返回item
字符串相關
-
{{'abc' | append: 'd'}} 在后面拼接字符串
-
{{'abc' | prepend: 'd'}} 在前面拼接字符串
-
{{'a b c' | camelize}} 將字符串轉化為駝峰命名方式
-
{{'a b c' | capitalize}} 字符串首字母大寫
-
{{'ABC' | downcase}} 轉化為小寫
-
{{'abc' | upcase}} 轉化為大寫
-
{{'<a>' | escape}} HTML字符串轉義
-
{{'this is a book' | handleize}} 將字符串轉化為'this-is-a-book'
-
{{'abcabc' | replace_first: 'a', 'x'}} 替換第一次出現的字符串a為x
-
{{'abcabc' | replace: 'a', 'x'}} 替換所有指定字符串a為x
-
{{'abcabc' | remove_first: 'a'}} 刪除第一次出現的指定字符串a
-
{{'abcabc' | remove: 'a'}} 刪除所有指定字符串a
-
{{'abc\nabc' | newline_to_br}} 將換行符轉換為
標簽 -
{{'a-b-c' | split: '-'}} 用指定字符串分割,返回數組
-
{{'abcd' | size}} 返回字符串長度
-
{{'123' | strip_html}} 去除HTML標簽,返回文本123
-
{{'abc\nabc' | strip_newlines}} 取出換行符
-
{{'abcdefg' | truncate: 3}} 取前N個字符
-
{{'this is a book' | truncatewords: 2}} 取前N個單詞
-
{{'abcdef' | reverse}} 反轉字符串
-
{{'ancdefg' | substr: N, M}} 從起始索引號N開始提取字符串中指定數目M的字符
日期時間相關
-
{{0 | timestamp}} 取當前毫秒時間戳,并加上0
-
{{'now' | date: '%H:%M%S'}} 格式化日期時間字符串
數組、對象相關
-
{{obj | keys}} 返回對象的所有鍵,結果為數組
-
{{array | first}} 返回數組(或對象)的第一個元素
-
{{array | last}} 返回數組(或對象)的最后一個元素
-
{{array | join: ','}} 將數組以指定的字符串拼接起來
-
{{array | size}} 返回數組長度
-
{{obj | json}} 轉換為JSON格式的字符串
-
{{obj | get: 'prop'}} 取對象的指定屬性
-
{{array | reverse}} 反轉數組
-
{{array | map: 'prop'}} 取數組各個元素的指定屬性,返回數組
-
{{array | sort: 'desc'}} 對數組進行排序,可為asc(升序)或desc(降序)
-
{{array | sort_by: 'prop', 'desc'}} 根據數組各個元素中的指定屬性排序
其他
- {{count | pagination: size, currentPage}} 生成導航頁碼