knockoutJS學習筆記02:jsRender模板引擎


  上一篇最后提到了模板,並嘗試自己編寫一個最簡單版本;有些朋友可能用過 jqtmpl,這是一個基於jquery的模板引擎,不過它已經不再更新了,而且據說渲染速度比較慢。這里介紹另外一個模板引擎:jsRender。個人覺得這些東西學習起來還是很簡單的,挑一個看看就行,實際要用到了哪個,官網看看demo也就會用了。之所以選擇jsRender,因為它具有以下特點:

  • 簡單直觀
  • 功能強大
  • 可擴展的
  • 快如閃電

  當然,誰都會自己給自己的產品下這樣定義。不過我用完后,確實發現它:簡單直觀、功能強大、擴展性強;至於快如閃電...,有興趣的朋友可以測試看看有多快!接下來就讓我們學習jsRender的運用,這里用的是1.0版本。

一、基礎

  jsRender 三個最重要的概念:模板、容器和數據。最重要的是:view(視圖) 是我們定義的模板,上下文是視圖所用的對象。

  先來看一下模板的基本標簽:{{:}} 和 {{>}}(或{{html:}})。兩者都可以輸出內容,不過后者是經過html編碼的。jsRender有幾種方式可以渲染數據:

  假設模板如下:

<script type="text/tmp" id="tmp1">
    <p>my name is: {{:name}}</p>
    <p>i am: {{:info.age}} years old</p>
</script>

   數據如下:  

    var data = {
        name : "tom",
        info : {age : 19}
    }

  1.無編譯前渲染。直接指定模板。例子:

    var html = $("#tmp1").render(data);
        console.log(html);

  2.編譯后渲染。指定模板或字符串。

    2.1 指定模板。效果同1。例子:

      var template = $.templates("#tmp1");
      var html = template.render(data);
      console.log(html);

    2.2 指定模板名稱

      2.2.1 傳遞字符串

        var template = $.templates("tmp1","<b>{{:name}}</b>");
            var html = $.render.tmp1(data);
            console.log(html);

      2.2.2 傳遞多個模板名稱

        var template = $.templates({
            "tmp1":$("#tmp1").html(),
            "tmp2":$("#tmp2").html()
           });
          var html = $.render.tmp1(data);    
          console.log(html);

二、邏輯判斷和循環

  2.1 if-else

  語法:{{if condition}} ... {else condition} ... {{else}}... {{/if}}

  注意這里是 if-else-else,而不是 if-else if-else。例子:  

<script type="text/tmp" id="tmp4">
    <p>my name is: {{:name}}</p>
    <p>我是:
    {{if info.age >= 18}}
        成年人
    {{else}}
        未成年
    {{/if}}
    </p>
</script>
var html = $("#tmp4").render(data);
$("#list").html(html);

  2.2 for

  語法:{{for}} ... {{/for}}

  2.2.1 簡單for。例子:  

    <script type="text/tmp" id="tmp5">
        {{for}}
            <li>id:{{:ID}} name:{{:Name}}</li>
        {{/for}}    
    </script>
    var arr = [
        { ID: 1, Name: "tom" },
        { ID: 2, Name: "jack" },
        { ID: 3, Name: "lucy"}
    ];    
    var html = $("#tmp5").render(arr);
    $("#list").html(html);    

  2.2.2 嵌套for。

  語法:{{for}}...{{for 當前上下文}} ... {{/for}} ... {{/for}}

  被嵌套的for 可以指定要遍歷的屬性的名稱。#getIndex() 和 #data 會在下面提到。例子:  

<script type="text/tmp" id="tmp7">    
    {{for}}
        <li>
            name:{{:name}}
            <ul>
                {{for hobbies}}
                    <li>{{:#getIndex() + 1}}:{{:#data}}</li>
                {{/for}}
            </ul>
        </li>
    {{/for}}    
</script>
arr = [
     { name: "tom", hobbies: ["籃球", "足球"] },
     { name: "jack", hobbies: ["籃球", "橄欖球"] },
     { name: "lucy", hobbies: ["游泳", "羽毛球"] }
];
var html = $("#tmp7").render(arr);
$("#list").html(html);

  2.2.3 分離for。

  語法:{{for 上下文 tmpl="模板id" /}}

  如果for的邏輯比較復雜,嵌套的for就會讓我們的模板變得復雜,維護難度加大;我們可以將for分離,以上面的例子,可以將for放到一個新的模板,然后通過 tmpl屬性指定。例子:

<script type="text/tmp" id="tmp8">    
    {{for}}
        <li>
            name:{{:name}}
            <ul>
                {{for hobbies tmpl="#tmp9" /}}
            </ul>
        </li>
    {{/for}}    
</script>
<script type="text/tmp" id="tmp9">    
    <li>{{:#getIndex() + 1}}:{{:#data}}</li>    
</script>
var html = $("#tmp8").render(arr);
$("#list").html(html);

三、#data、#index、#parent

  開始提到,我們定義的模板就是視圖,用到的對象就是上下文。

  #data 當前的上下文,有時候它是很有用的,例如我們返回的是最簡單的數組[1,2,3,4],這時沒法通過{{:屬性}} 的形式去獲得,通過{{#data}}即可。另外,可以指定上下文,例如上面的 hobbies,內嵌的for的上下文 #data 就是hobbies,而外部的 #data 是整個arr。

  #index 當前下標。應該用 #getIndex() 去獲得。

  #parent 當前上下文所在的視圖。parent 屬性可以一直往上查找視圖,視圖的data屬性就是當前上下文。例如如果我們要在內嵌的for獲得外部的name屬性,就可以通過 #parent.parent.data.name 獲得。

四、擴展應用

  上面的基本用法已經可以滿足大部分需求了。以下幾個擴展都是為了分離視圖和邏輯的,試想一下,如果我們的視圖里還需要大量的邏輯判斷或計算,全都寫在一起,那會非常麻煩和難以維護。視圖最好就是簡單的標簽,而邏輯都寫在js里。jsRender是在視圖上進行擴展的。

4.1 自定義標簽 Tag

  我們可以把標簽里的邏輯移到自定義標簽里。

  語法:1.視圖 {{"標簽名稱" 標簽參數 附加參數}}
           2.邏輯 $.views.tags({"標簽名稱":function(參數){this.tagCtx.props.prefix附加參數}}) 

  例子:

    <script type="text/tmp" id="tmp10">
        {{for}}
            <li>
                name:{{:name}}                            
                hobbies: {{format hobbies prefix="@" /}}
            </li>
        {{/for}}         
    </script>
    $.views.tags({
        "format":function(hobbies){
            if(!hobbies || hobbies.length <= 0){
                return "無";
            }
            var result = "";
            for(var i = 0,length = hobbies.length;i < length; i++){
                result += "," + this.tagCtx.props.prefix + hobbies[i];
            }
            result = result.substring(1);
            return result;
        }
    })
    var html = $("#tmp10").render(arr);
    $("#list").html(html);

4.2 轉換器 converter

  轉換器可以對輸出結果進行處理,例如大小寫轉換等。

  語法:1. 視圖 {{"轉化器名稱":參數}}
          2. js $.views.converters({"轉換器名稱":function(參數){...}})

    <script type="text/tmp" id="tmp11">    
        {{for}}
            <li>
                Upper Name: {{toUpper:#parent.data.name}}
            </li>
        {{/for}}   
    </script>
    $.views.converters({
        "toUpper":function(name){
            if(name){
                return name.toUpperCase();
            }
        }
    })
    var html = $("#tmp11").render(arr);
    $("#list").html(html)

4.3 輔助函數 Helper

  擴展函數可以對結果進行處理,邏輯判斷等等。

  語法 1. 視圖 {{~輔助函數名稱(參數)}}
          2. js $.views.helpers({"輔助函數名稱":function(參數){}})

    <script type="text/tmp" id="tmp12">    
        {{for}}
            <li>
                name: {{:name}}
                hobbies: {{:~concat(hobbies)}}
            </li>
        {{/for}}   
    </script>
    $.views.helpers({
        "concat":function(hobbies){            
            if(!hobbies || hobbies.length <= 0){
                return "";
            }
            var result = "";
            for(var i = 0,length = hobbies.length;i < length;i++){
                result += "&&" + hobbies[i];
            }
            return result.substring(2);
        }
    })
    var html = $("#tmp12").render(arr);
    $("#list").html(html);

五、總結

  jsRender還是比較新的,使用起來也比較方便。不過我們看到我們的數據和頁面之間是一個單向的過程,而且是一次性的單向;也就是當我們的數據改變時,視圖不能自動更新,而界面的數據改變時,實際數據也不能自動改變。設想一下,如果數據和視圖是雙向的,其中一個改變,都可以自動更新另外一個,那不就最好了,我們只需要操作數據,不需要操作dom了。這就是mvvm的機制,前端的mvvm框架有很多:knockout、angular等。下一篇將開始介紹knockout。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM