一個兼容Liquid語法的模板引擎

jopen 11年前發布 | 36K 次閱讀 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的自動獲取數據功能可以解決該問題。以下為使用不同方式異步獲取數據的例子:

使用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}} 生成導航頁碼

項目主頁:http://www.baiduhome.net/lib/view/home/1361323129134

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!