編寫javascript模板引擎的幾個步驟

catpl

先推薦一個我本身寫的模板引擎catpl,項目地址:https://github.com/liyu365/catpl
項目中有詳細的註釋,代碼易讀,本項目借鑑自artTemplate、juicer、laytpl等諸多項目,在學習這些項目的過程當中編寫而成,並遵照如下幾個步驟,能夠對照此項目代碼進行了解。javascript

下面就是總結的編寫javascript模板引擎的幾個步驟

例如一個模板爲:html

<h2><%= title %></h2>
<ul>
<% for(var i = 0 ; i < sports.length ; i++ ) { %>
    <li><% i + 1 %> - <% sports[i].name %></li>
<% } %>
</ul>

最終會編譯成爲一個函數:java

function anonymous($data, $methods) {
    var title = $data.title, sports = $data.sports, $escape = $methods.$escape;
    var $out = '';
    $out += '<h2>';
    $out += $escape(title);
    $out += '</h2><ul>';
    for(var i = 0 ; i < sports.length ; i++ ) {
        $out += '<li>';
        $out += $escape(i + 1);
        $out += ' - ';
        $out += $escape(sports[i].name);
        $out += '</li>';
    }
    $out += '</ul>';
    return $out;
}

能夠觀察到模板中的全部的變量名都被指定成了參數$data對象的屬性,而且該函數自始至終只作了一件事,就是構建$out字符串,並將其返回。下面來梳理一下將模板編譯爲函數的過程:git

步驟1. 確立模板中邏輯表達式的openTag和closeTag

在上面的例子中<%%>就是openTag和closeTag。github

任何的HTML模板都包括兩個部分:函數

  • HTML語句工具

  • 邏輯表達式語句學習

openTag和closeTag的做用就是劃分出這兩個部分區別對待。<%%>包裹着的爲邏輯表達式語句,在其以外的是普通的HTML語句。優化

步驟2. 拆分"HTML語句"和"邏輯表達式語句"

methods.$foreach(source.split(options.openTag), function (code) {
    var arr = code.split(options.closeTag);
    if (arr.length === 1) {
        code_body += handle_html(arr[0]);
    } else {
        code_body += handle_logic(arr[0]);
        if (arr[1]) {
            code_body += handle_html(arr[1]);
        }
    }
});

能夠看到這一步利用openTag和closeTag把模板源碼中的HTML語句傳給了handle_html()函數,把邏輯表達式語句傳給了handle_logic()函數。這兩個函數會把返回的代碼拼接給一個統一的變量。ui

步驟3. 處理模板中的"HTML語句"

對應上面提到的handle_html()函數。

這一步很好理解,只要原樣輸出就好。

步驟4. 處理模板中的"邏輯表達式語句"

對應上面提到的handle_logic()函數,這一步的核心就是分析邏輯表達式語句中的變量名,函數名,識別的方式很簡單,只要不是js中的關鍵字、保留字都視爲是一個變量名。

邏輯表達式語句又分爲兩種:

1)賦值語句,形如<%= title %>

直接把title視爲是一個變量名,在函數的開始處聲明title爲$data.title的引用。而後對title這個字符串原樣輸出。由於$data.title對應的值可能帶有html中的特殊字符,因此在title外面包裹了$escape函數的調用。$escape函數是一個咱們預先編寫好的函數,存放在methods對象中,而後傳和data同樣傳入到編譯函數中。

2) 邏輯控制語句,形如for(var i = 0 ; i < sports.length ; i++ ) {

依然要分析變量名,例如這裏的sports,注意分析變量名時必定要忽略.length這樣的對象屬性。

步驟5.生成編譯函數

利用Function構造函數生成一個函數:

var fun = new Function('$data', '$method', code);

這裏的code固然就是handle_html()handle_logic()兩個方法翻譯後的標準js代碼拼接而成的。
fun函數還須要進一步封裝,畢竟$method尚未值。第四部已經提到,methods實際上是咱們模板引擎內部的對象,存放一些模板引擎的工具方法,咱們應該本身把它傳給編譯好的函數。其實給模板引擎註冊helper方法也是同樣的,須要咱們本身把helper對象傳進編譯函數。包裝代碼以下:

return function (data) {
    return fun(data, methods, helpers);
};

此時返回的函數就是用戶實際拿到的函數。

步驟6.進一步優化"邏輯表達式語句"的語法

其實作到第五步已是一個具有基本功能的模板引擎了,可是模板中邏輯表達式不夠簡潔,例如:

<% for(var i = 0 ; i < sports.length ; i++ ) { %>

這種表達式其實仍是原生的js語法,咱們能夠本身定義模板引擎的語法,例如:

<% foreach sports %>

這其實不難作到,只是在第四部開始以前利用正則把foreach sports再變爲for(var i = 0 ; i < sports.length ; i++ ) {,以後仍是同樣的流程。

catpl項目中有一個options.syntax_hook方法就是用來定義本身的模板語法的,能夠將options.syntax_hook的值設爲null來在自定義語法和原生js語法之間切換。

相關文章
相關標籤/搜索