SlideShare una empresa de Scribd logo
1 de 60
Descargar para leer sin conexión
一个前端 Javascript 模板引擎的实现与优化



                         流火 @ TaobaoUED
                         Blog: http://benben.cc




12年5月28日星期⼀一
Who am i ?
     【诗经・国风・豳风】 七月流火,九月授衣。


      Blog: http://benben.cc
      Github: http://github.com/PaulGuo
      Weibo: http://weibo.com/janbing
      Twitter: http://twitter.com/guokai




     【一些开源项目】




     【在哪】




12年5月28日星期⼀一
doT     yayaTemplate
                    ejs
                             Mustache
                                              artTemplate
               nTenjin     handlebar
                                    Kissy template
                   micro template

                  ...
                               jQuery tmpl



12年5月28日星期⼀一
is it nessary ? are u sure ?

               如无必要,勿增实体




12年5月28日星期⼀一
Juicer 的特点                            support node.js only 5kb




          1 循环   {@each}…{@/each}

          2 判断   {@if}…{@else if}…{@else}…{@/if}

          3 变量(支持自定义函数) ${varname|function}

          4 注释   {# comment here}

          5 辅助循环     {@each i in range(0,9)}

          6 自定义函数,扩展灵活              register/unregister




12年5月28日星期⼀一
12年5月28日星期⼀一
模板引擎基本原理

     模板引擎的基本机理就是替换(转换),将指定的标签转换为
     需要的业务数据;将指定的伪语句按照某种流程来变换输出。


      The gist of JavaScript templates is that you can take an HTML fragment interpolated
      with template variables and combine it with a JavaScript object, replacing those
      tem- plate variables with values from the object. Overall, JavaScript templating
      works in much the same way as templating libraries in other languages, such as
      PHP’s Smarty, Ruby’s ERB, and Python’s string formatting.

      - Javascript Web Application




12年5月28日星期⼀一
water     juicer     fruits



                       Template
               JSON     Engine
                                  Template




                         HTML
                       Fragment




12年5月28日星期⼀一
模板引擎的分类


      ·置换型模板引擎。

      ·解释型模板引擎。

      ·编译型模板引擎。




12年5月28日星期⼀一
静态页面   含有丰富交互的应用      服务端
                  后端提供纯数据(接口化)
                   前端负责将数据生成页面




12年5月28日星期⼀一
MVC 需要模板

     MVC最早是在SmallTalk语言的开发过程中总结出的一种设
     计模式,MVC分别代表了"模型"、"视图"和"控制",目的就
     是让不同的开发角色在大中型项目中各司其职。在网络应
     用程序的开发中,可以用下图来表示各概念之间的关系。




12年5月28日星期⼀一
现实中的使用场景




12年5月28日星期⼀一
拼字符串


      var json = { name: "liuhuo", blog: "ued.taobao.com"};



      var html = '<span class="name">' + json.name + ' (blog: ' + json.blog + ')</span>';



      <span class="name">流火 (blog: ued.taobao.com)</span>




12年5月28日星期⼀一
YUI.Lang.sub

      function sub(str,data) {
          return str
              .replace(/{(.*?)}/igm,function($,$1) {
                  return data[$1]?data[$1]:$;
              });
      }




      var tpl = '<span class="name">{name} (blog: {blog})</span>';

      var html = sub(tpl, json);




12年5月28日星期⼀一
Security
                                       XSS escape




                Syntax                              Performance
               easy to write & read                 as faster as you can




                                Error Handling
                                       robustness




12年5月28日星期⼀一
语法



          主张原生语法的传统模板引擎                                                           主张Logic-less的新兴模板引擎

                   ejs、nTenjin、doT                                                      mustache、handlebars
                                                                                                    {{#list}}
                                                                                           {{#a}} {{value}} {{/a}}

               追求纯粹,希望通过原生的                                                           追求 KISS / Logic less, 这注定了这
               Javascript 语法来写模板,这类模                                                  类模板引擎的实现是解释型的,
               板引擎的实现一定是编译型的,                                                         也就注定了它们性能是有瓶颈
               但是它们的写法往往让人抓狂。                                                         的。




                                               jQuery Tmpl、kissy Template



               the templating syntax for most libraries is very similar, if not identical. - Javascript Web Application

12年5月28日星期⼀一
JSON                                  HTML

      var data={
                                            <ul>
          list:[
                                                <li>liuhuo (index: 0)</li>
              {name:'liuhuo',show:true},
                                                <li>taobao (index: 2)</li>
              {name:'benben',show:false},
                                                <li>num: 1</li>
              {name:'taobao',show:true}
                                                <li>num: 2</li>
          ],
                                                <li>num: 3</li>
          blah:[
                                                <li>15:00</li>
              {num:1},
                                                <li>16:00</li>
              {num:2},
                                                <li>17:00</li>
              {num:3,inner:[
                                                <li>18:00</li>
                  {'time':'15:00'},
                                                <li>num: 4</li>
                  {'time':'16:00'},
                                            </ul>
                  {'time':'17:00'},
                  {'time':'18:00'}
              ]},
              {num:4}
          ]
      };




12年5月28日星期⼀一
语法 |             doT、nTenjin、ejs、tmpl




       <ul>
           <?js for(var i=0;i<it.list.length;i++) { ?>
               <?js if(it.list[i].show) { ?>
                   <li>${it.list[i].name} (index: ${i})</li>
               <?js } ?>
           <?js } ?>
           <?js for(var i=0;i<it.blah.length;i++) { ?>
               <li>
                   num: ${it.blah[i].num}
                   <?js if(it.blah[i].num==3) { ?>           
                       <?js for(var j=0;j<it.blah[i].inner.length;j++) { ?>
                           <li>${it.blah[i].inner[j].time}</li>
                       <?js } ?>
                   <?js } ?>
               </li>
           <?js } ?>
       </ul>




12年5月28日星期⼀一
语法 |             doT、nTenjin、ejs、tmpl




       <ul>
           <% for(var i=0;i<list.length;i++) { %>
               <% if(list[i].show) { %>
                   <li><%= list[i].name %> (index: <%= i %>)</li>
               <% } %>
           <% } %>
           <% for(var i=0;i<blah.length;i++) { %>
               <li>
                   num: <%= blah[i].num %>
                   <% if(blah[i].num==3) { %>           
                       <% for(var j=0;j<blah[i].inner.length;j++) { %>
                           <li><%= blah[i].inner[j].time %></li>
                       <% } %>
                   <% } %>
               </li>
           <% } %>
       </ul>




12年5月28日星期⼀一
语法 |             mustache、handlebars




       <ul>
           {{#list}}
               {{#show}}
                   <li>{{name}} (index: {{index}})</li>
               {{/show}}
           {{/list}}
           {{#blah}}
               <li>
                   num: {{num}}
                   ...
                   {{#inner}}
                       <li>{{time}}</li>
                   {{/inner}}
                   ...
               </li>
           {{/blah}}
       </ul>




12年5月28日星期⼀一
语法 |             kissy、juicer




       <ul>
           {{#each list as it,k}}
               {{#if it.show}}
                   <li>{{it.name}} (index: {{k}})</li>
               {{/if}}
           {{/each}}
           {{#each blah as it}}
               <li>
                   num: {{it.num}}
                   {{#if it.num==3}}
                       {{#each it.inner as it2}}
                           <li>{{it2.time}}</li>
                       {{/each}}
                   {{/if}}
               </li>
           {{/each}}
       </ul>




12年5月28日星期⼀一
语法 |             kissy、juicer




       <ul>
           {@each data.list as it,k}
               {@if it.show}
                   <li>${it.name} (index: ${k})</li>
               {@/if}
           {@/each}
           {@each data.blah as it}
               <li>
                   num: ${it.num}
                   {@if it.num==3}
                       {@each it.inner as it2}
                           <li>${it2.time}</li>
                       {@/each}
                   {@/if}
               </li>
           {@/each}
       </ul>




12年5月28日星期⼀一
立场


       语法上:不赞同完全采用原生语法,也不认可过于 Logic-less 从而在性能上遇到瓶颈。
       性能上:认为模板引擎的性能应该是第⼀一位的。



       The basic advice regarding response times has been about the same for thirty years [Miller
       1968; Card et al. 1991]:


         • 0.1 second is about the limit for having the user feel that the system is reacting
           instantaneously, meaning that no special feedback is necessary except to display the result.
         • 1.0 second is about the limit for the user's flow of thought to stay uninterrupted, even
            though the user will notice the delay. Normally, no special feedback is necessary during
            delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of
            operating directly on the data.




       100ms * 100w = 27.777hours




12年5月28日星期⼀一
性能




12年5月28日星期⼀一
性能 -     最大化渲染测试




12年5月28日星期⼀一
性能优化点




               using += instead of array.push




12年5月28日星期⼀一
str += "one" + "two";


      When evaluating this code, four steps are taken:


      1. A temporary string is created in memory.
      2. The concatenated value "onetwo" is assigned to the temporary string.
      3. The temporary string is concatenated with the current value of str.
      4. The result is assigned to str.

      - Hign Performance Javascript




12年5月28日星期⼀一
str = ["one", "two"].join(‘’);




      This dramatic improvement results from avoiding repeatedly allocating memory for
      and copying progressively larger and larger strings. When joining an array, the
      browser allocates enough memory to hold the complete string, and never copies the
      same part of the final string more than once.

      - Hign Performance Javascript




12年5月28日星期⼀一
12年5月28日星期⼀一
Browser string optimizations have changed the string
               concatenation picture.


               Firefox was the first browser to optimize string concatenation. Beginning with
               version 1.0, the array technique is actually slower than using the plus operator
               in all cases. Other browsers have also optimized string concatenation, so Safari,
               Opera, Chrome, and Internet Explorer 8 also show better performance using
               the plus operator. Internet Explorer prior to version 8 didn’t have such an
               optimization, and so the array technique is always faster than the plus operator.



               — Writing Efficient JavaScript: Chapter 7 – Even Faster Websites




12年5月28日星期⼀一
The V8 engine (used in Google Chrome) uses this code to do string
               concatenation:



               // ECMA-262, section 15.5.4.6
               function StringConcat() {
                 if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
                   throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
                 }
                 var len = %_ArgumentsLength();
                 var this_as_string = TO_STRING_INLINE(this);
                 if (len === 1) {
                   return this_as_string + %_Arguments(0);
                 }
                 var parts = new InternalArray(len + 1);
                 parts[0] = this_as_string;
                 for (var i = 0; i < len; i++) {
                   var part = %_Arguments(i);
                   parts[i + 1] = TO_STRING_INLINE(part);
                 }
                 return %StringBuilderConcat(parts, len + 1, "");
               }




12年5月28日星期⼀一
性能优化点




               avoid using with block




12年5月28日星期⼀一
12年5月28日星期⼀一
var person = {                        The with construct introduces an extra scope for
             name: "Nicholas",                 the script engine to search through whenever a
             age: 30                           variable is referenced. This alone produces a
         };                                    minor performance decrease. However, the
                                               contents of that scope are not known at compile
         function displayInfo(){               time, meaning that the compiler cannot optimize
             var count = 5;                    for it, in the same way as it can with normal scopes
             with(person){                     (such as those created by functions).
                 alert(name + " is " + age);
                 alert("Count is " + count);
             }
         }

         displayInfo();




12年5月28日星期⼀一
性能优化点




               cache the compiled template.




12年5月28日星期⼀一
Cache The Compiled Template



      JavaScript, like many scripting languages, allows you to take a string containing
      code and execute it from within running code. There are four standard ways to
      accomplish this: eval(), the Function() constructor, setTimeout(), and setInterval().
      Each of these functions allows you to pass in a string of JavaScript code and have it
      executed.

      - Hign Performance Javascript




12年5月28日星期⼀一
安全性


     var json={
     !    output:'<script>alert("XSS");</script>'
     };




      document.write                          $(node).html()

      juicer('${output}',json);
      //输出:&lt;script&gt;alert("XSS");&lt;/script&gt;

      juicer('$${output}',json);
      //输出:<script>alert("XSS");</script>




12年5月28日星期⼀一
Juicer 的使用方法.




12年5月28日星期⼀一
12年5月28日星期⼀一
12年5月28日星期⼀一
12年5月28日星期⼀一
{@each list as it, k}
                   <span class=”{@if k+2>list.length}notdot{@/if}”>...</span>
               {@/each}




12年5月28日星期⼀一
http://taobao.com/s?q=%E6%B7%98%E5%AE%9D




               json={query:’淘宝’,...}
               http://taobao.com/s?q=${query|encodeURIComponent}




12年5月28日星期⼀一
12年5月28日星期⼀一
12年5月28日星期⼀一
12年5月28日星期⼀一
compile                             render
        template             reusable function                  html code
                                   compiled template




12年5月28日星期⼀一
单一接口


          (function() {
                var juicer = function() {
                      var args = [].slice.call(arguments);
                      args.push(juicer.options);


                      if(arguments.length == 1) {
                          return juicer.compile.apply(juicer, args);
                      }


                      if(arguments.length >= 2) {
                          return juicer.to_html.apply(juicer, args);
                      }
                };


                ...
          ...




12年5月28日星期⼀一
编译

               juicer.compile = function(tpl, options) {
                   if(!options || options !== this.options) {
                       options = __creator(options, this.options);
                   }

                    try {
                        var engine = this.__cache[tpl] ?
                            this.__cache[tpl] :
                            new this.template(this.options).parse(tpl, options);

                        if(!options || options.cache !== false) {
                            this.__cache[tpl] = engine;
                        }

                        return engine;

                    } catch(e) {
                        __throw('Juicer Compile Exception: ' + e.message);

                        return {
                            render: function() {} //noop
                        };
                    }
               };



12年5月28日星期⼀一
编译


               this.parse = function(tpl, options) {
                   var _that = this;

                    if(!options || options.loose !== false) {
                        tpl = this.__lexicalAnalyze(tpl) + tpl;
                    }

                    tpl = this.__removeShell(tpl, options);
                    tpl = this.__toNative(tpl, options);

                    this._render = new Function('_, _method', tpl);

                    this.render = function(_, _method) {
                        if(!_method || _method !== that.options._method) {
                            _method = __creator(_method, that.options._method);
                        }

                         return _that._render.call(this, _, _method);
                    };

                    return this;
               };




12年5月28日星期⼀一
before __removeShell




               <ul>

                   {@each list as it,k}

                       {@if it.show}

                           <li>${it.name} (index: ${k})</li>

                       {@/if}

                   {@/each}

               </ul>




12年5月28日星期⼀一
after __removeShell




               <ul>
                   <% for var i0 = 0; i0 < list; i0++ { %>
                       <% var it = list[i0]; %>
                       <% var k = i0; %>
                       <% if(it.show) { %>
                           <li><%= it.name %> (index: <%= k %>)</li>
                       <% } %>
                   <% } %>
               </ul>




12年5月28日星期⼀一
__toNative
       function anonymous(_, _method) {
       	   'use strict';
       	   var _ = _ || {};
       	   var _out = '';
       	   _out += '';
       	   try {
       	   	    _out += '';
       	   	    var list = _.list;
       	   	    var it = _.it;       __lexicalAnalyze
       	   	    var k = _.k;
       	   	    _out += '<ul>';
       	   	    for (var i0 = 0, li0 = list.length; i0 < li0; i0++) {
       	   	    	   var it = list[i0];
       	   	    	   var k = i0;
       	   	    	   _out += '';
       	   	    	   if (it.show) {
       	   	    	   	    _out += '<li>';
       	   	    	   	    _out += _method.__escapehtml.escaping(_method.__escapehtml.detection((it.name)));
       	   	    	   	    _out += ' (index: ';
       	   	    	   	    _out += _method.__escapehtml.escaping(_method.__escapehtml.detection((k)));
       	   	    	   	    _out += ')</li> ';
       	   	    	   }
       	   	    	   _out += '';
       	   	    }
       	   	    _out += '</ul>';
       	   } catch (e) {
       	   	    _method.__throw("Juicer Render Exception: " + e.message);
       	   }
       	   _out += '';
       	   return _out;
       }

12年5月28日星期⼀一
渲染函数


               this.parse = function(tpl, options) {
                   var _that = this;

                    if(!options || options.loose !== false) {
                        tpl = this.__lexicalAnalyze(tpl) + tpl;
                    }

                    tpl = this.__removeShell(tpl, options);
                    tpl = this.__toNative(tpl, options);

                    this._render = new Function('_, _method', tpl);

                    this.render = function(_, _method) {
                        if(!_method || _method !== that.options._method) {
                            _method = __creator(_method, that.options._method);
                        }

                         return _that._render.call(this, _, _method);
                    };

                    return this;
               };




12年5月28日星期⼀一
参数/方法继承



               var __creator = function(o, proto) {
                   o = o !== Object(o) ? {} : o;

                    if(o.__proto__) {
                        o.__proto__ = proto;
                        return o;
                    }

                    var _Empty = function() {};
                    var n = new((_Empty).prototype = proto, _Empty);

                    for(var i in o) {
                        if(o.hasOwnProperty(i)) {
                            n[i] = o[i];
                        }
                    }

                    return n;
               };




12年5月28日星期⼀一
编译并渲染




               juicer.to_html = function(tpl, data, options) {
                    if(!options || options !== this.options) {
                        options = __creator(options, this.options);
                    }


                    return this.compile(tpl, options).render(data, options._method);
               };




12年5月28日星期⼀一
12年5月28日星期⼀一
this.parse = function(tpl, options) {
                   var _that = this;

                    if(!options || options.loose !== false) {
                        tpl = this.__lexicalAnalyze(tpl) + tpl;
                    }

                    tpl = this.__removeShell(tpl, options);
                    tpl = this.__toNative(tpl, options);

                    this._render = new Function('_, _method', tpl);

                    this.render = function(_, _method) {
                        var fn = function() {};

                         fn.toString = function() {
                             if(!_method || _method !== that.options._method) {
                                 _method = __creator(_method, that.options._method);
                             }

                              return _that._render.call(this, _, _method);
                         };

                         return fn;
                    };

                    return this;
               };




12年5月28日星期⼀一
展望


      ·继续优化 Juicer (性能、源代码)

      ·完善更多的文档和实利

      ·提供其它模板语法到 Juicer 模板的自动化工具

      ·更多的语言支持(诸如PHP、Python、Ruby …)

      ·精确的模板调试模式,精确到行号和模板错误位置

      ·持续改进




12年5月28日星期⼀一
Q&A

               juicer(‘${over}’, {over:‘thanks!’});




12年5月28日星期⼀一

Más contenido relacionado

Similar a Juicer - A fast template engine using javascript

数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点thinkinlamp
 
Mastering Mustache
Mastering MustacheMastering Mustache
Mastering Mustachetinyhill
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现hua qiu
 
Javascript模板引擎
Javascript模板引擎Javascript模板引擎
Javascript模板引擎Jerry Xie
 
Jquery指南
Jquery指南Jquery指南
Jquery指南yiditushe
 
AngularJS Sharing
AngularJS SharingAngularJS Sharing
AngularJS SharingTom Chen
 
Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012Mu-Fan Teng
 
HTML5 介绍
HTML5 介绍HTML5 介绍
HTML5 介绍S S
 
一拍一产品背后的故事(React实战)
一拍一产品背后的故事(React实战)一拍一产品背后的故事(React实战)
一拍一产品背后的故事(React实战)Kejun Zhang
 

Similar a Juicer - A fast template engine using javascript (11)

数据处理算法设计要点
数据处理算法设计要点数据处理算法设计要点
数据处理算法设计要点
 
Mastering Mustache
Mastering MustacheMastering Mustache
Mastering Mustache
 
旺铺前端设计和实现
旺铺前端设计和实现旺铺前端设计和实现
旺铺前端设计和实现
 
Javascript模板引擎
Javascript模板引擎Javascript模板引擎
Javascript模板引擎
 
Jquery指南
Jquery指南Jquery指南
Jquery指南
 
AngularJS Sharing
AngularJS SharingAngularJS Sharing
AngularJS Sharing
 
Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012Concurrency model for mysql data processing@rubyconf.tw 2012
Concurrency model for mysql data processing@rubyconf.tw 2012
 
getPDF.aspx
getPDF.aspxgetPDF.aspx
getPDF.aspx
 
getPDF.aspx
getPDF.aspxgetPDF.aspx
getPDF.aspx
 
HTML5 介绍
HTML5 介绍HTML5 介绍
HTML5 介绍
 
一拍一产品背后的故事(React实战)
一拍一产品背后的故事(React实战)一拍一产品背后的故事(React实战)
一拍一产品背后的故事(React实战)
 

Juicer - A fast template engine using javascript

  • 1. 一个前端 Javascript 模板引擎的实现与优化 流火 @ TaobaoUED Blog: http://benben.cc 12年5月28日星期⼀一
  • 2. Who am i ? 【诗经・国风・豳风】 七月流火,九月授衣。 Blog: http://benben.cc Github: http://github.com/PaulGuo Weibo: http://weibo.com/janbing Twitter: http://twitter.com/guokai 【一些开源项目】 【在哪】 12年5月28日星期⼀一
  • 3. doT yayaTemplate ejs Mustache artTemplate nTenjin handlebar Kissy template micro template ... jQuery tmpl 12年5月28日星期⼀一
  • 4. is it nessary ? are u sure ? 如无必要,勿增实体 12年5月28日星期⼀一
  • 5. Juicer 的特点 support node.js only 5kb 1 循环 {@each}…{@/each} 2 判断 {@if}…{@else if}…{@else}…{@/if} 3 变量(支持自定义函数) ${varname|function} 4 注释 {# comment here} 5 辅助循环 {@each i in range(0,9)} 6 自定义函数,扩展灵活 register/unregister 12年5月28日星期⼀一
  • 7. 模板引擎基本原理 模板引擎的基本机理就是替换(转换),将指定的标签转换为 需要的业务数据;将指定的伪语句按照某种流程来变换输出。 The gist of JavaScript templates is that you can take an HTML fragment interpolated with template variables and combine it with a JavaScript object, replacing those tem- plate variables with values from the object. Overall, JavaScript templating works in much the same way as templating libraries in other languages, such as PHP’s Smarty, Ruby’s ERB, and Python’s string formatting. - Javascript Web Application 12年5月28日星期⼀一
  • 8. water juicer fruits Template JSON Engine Template HTML Fragment 12年5月28日星期⼀一
  • 9. 模板引擎的分类 ·置换型模板引擎。 ·解释型模板引擎。 ·编译型模板引擎。 12年5月28日星期⼀一
  • 10. 静态页面 含有丰富交互的应用 服务端 后端提供纯数据(接口化) 前端负责将数据生成页面 12年5月28日星期⼀一
  • 11. MVC 需要模板 MVC最早是在SmallTalk语言的开发过程中总结出的一种设 计模式,MVC分别代表了"模型"、"视图"和"控制",目的就 是让不同的开发角色在大中型项目中各司其职。在网络应 用程序的开发中,可以用下图来表示各概念之间的关系。 12年5月28日星期⼀一
  • 13. 拼字符串 var json = { name: "liuhuo", blog: "ued.taobao.com"}; var html = '<span class="name">' + json.name + ' (blog: ' + json.blog + ')</span>'; <span class="name">流火 (blog: ued.taobao.com)</span> 12年5月28日星期⼀一
  • 14. YUI.Lang.sub function sub(str,data) { return str .replace(/{(.*?)}/igm,function($,$1) { return data[$1]?data[$1]:$; }); } var tpl = '<span class="name">{name} (blog: {blog})</span>'; var html = sub(tpl, json); 12年5月28日星期⼀一
  • 15. Security XSS escape Syntax Performance easy to write & read as faster as you can Error Handling robustness 12年5月28日星期⼀一
  • 16. 语法 主张原生语法的传统模板引擎 主张Logic-less的新兴模板引擎 ejs、nTenjin、doT mustache、handlebars {{#list}} {{#a}} {{value}} {{/a}} 追求纯粹,希望通过原生的 追求 KISS / Logic less, 这注定了这 Javascript 语法来写模板,这类模 类模板引擎的实现是解释型的, 板引擎的实现一定是编译型的, 也就注定了它们性能是有瓶颈 但是它们的写法往往让人抓狂。 的。 jQuery Tmpl、kissy Template the templating syntax for most libraries is very similar, if not identical. - Javascript Web Application 12年5月28日星期⼀一
  • 17. JSON HTML var data={ <ul>     list:[     <li>liuhuo (index: 0)</li>         {name:'liuhuo',show:true},     <li>taobao (index: 2)</li>         {name:'benben',show:false},     <li>num: 1</li>         {name:'taobao',show:true}     <li>num: 2</li>     ],     <li>num: 3</li>     blah:[     <li>15:00</li>         {num:1},     <li>16:00</li>         {num:2},     <li>17:00</li>         {num:3,inner:[     <li>18:00</li>             {'time':'15:00'},     <li>num: 4</li>             {'time':'16:00'}, </ul>             {'time':'17:00'},             {'time':'18:00'}         ]},         {num:4}     ] }; 12年5月28日星期⼀一
  • 18. 语法 | doT、nTenjin、ejs、tmpl <ul>     <?js for(var i=0;i<it.list.length;i++) { ?>         <?js if(it.list[i].show) { ?>             <li>${it.list[i].name} (index: ${i})</li>         <?js } ?>     <?js } ?>     <?js for(var i=0;i<it.blah.length;i++) { ?>         <li>             num: ${it.blah[i].num}             <?js if(it.blah[i].num==3) { ?>                            <?js for(var j=0;j<it.blah[i].inner.length;j++) { ?>                     <li>${it.blah[i].inner[j].time}</li>                 <?js } ?>             <?js } ?>         </li>     <?js } ?> </ul> 12年5月28日星期⼀一
  • 19. 语法 | doT、nTenjin、ejs、tmpl <ul>     <% for(var i=0;i<list.length;i++) { %>         <% if(list[i].show) { %>             <li><%= list[i].name %> (index: <%= i %>)</li>         <% } %>     <% } %>     <% for(var i=0;i<blah.length;i++) { %>         <li>             num: <%= blah[i].num %>             <% if(blah[i].num==3) { %>                            <% for(var j=0;j<blah[i].inner.length;j++) { %>                     <li><%= blah[i].inner[j].time %></li>                 <% } %>             <% } %>         </li>     <% } %> </ul> 12年5月28日星期⼀一
  • 20. 语法 | mustache、handlebars <ul>     {{#list}}         {{#show}}             <li>{{name}} (index: {{index}})</li>         {{/show}}     {{/list}}     {{#blah}}         <li>             num: {{num}}             ...             {{#inner}}                 <li>{{time}}</li>             {{/inner}}             ...         </li>     {{/blah}} </ul> 12年5月28日星期⼀一
  • 21. 语法 | kissy、juicer <ul>     {{#each list as it,k}}         {{#if it.show}}             <li>{{it.name}} (index: {{k}})</li>         {{/if}}     {{/each}}     {{#each blah as it}}         <li>             num: {{it.num}}             {{#if it.num==3}}                 {{#each it.inner as it2}}                     <li>{{it2.time}}</li>                 {{/each}}             {{/if}}         </li>     {{/each}} </ul> 12年5月28日星期⼀一
  • 22. 语法 | kissy、juicer <ul>     {@each data.list as it,k}         {@if it.show}             <li>${it.name} (index: ${k})</li>         {@/if}     {@/each}     {@each data.blah as it}         <li>             num: ${it.num}             {@if it.num==3}                 {@each it.inner as it2}                     <li>${it2.time}</li>                 {@/each}             {@/if}         </li>     {@/each} </ul> 12年5月28日星期⼀一
  • 23. 立场 语法上:不赞同完全采用原生语法,也不认可过于 Logic-less 从而在性能上遇到瓶颈。 性能上:认为模板引擎的性能应该是第⼀一位的。 The basic advice regarding response times has been about the same for thirty years [Miller 1968; Card et al. 1991]: • 0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result. • 1.0 second is about the limit for the user's flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of operating directly on the data. 100ms * 100w = 27.777hours 12年5月28日星期⼀一
  • 25. 性能 - 最大化渲染测试 12年5月28日星期⼀一
  • 26. 性能优化点 using += instead of array.push 12年5月28日星期⼀一
  • 27. str += "one" + "two"; When evaluating this code, four steps are taken: 1. A temporary string is created in memory. 2. The concatenated value "onetwo" is assigned to the temporary string. 3. The temporary string is concatenated with the current value of str. 4. The result is assigned to str. - Hign Performance Javascript 12年5月28日星期⼀一
  • 28. str = ["one", "two"].join(‘’); This dramatic improvement results from avoiding repeatedly allocating memory for and copying progressively larger and larger strings. When joining an array, the browser allocates enough memory to hold the complete string, and never copies the same part of the final string more than once. - Hign Performance Javascript 12年5月28日星期⼀一
  • 30. Browser string optimizations have changed the string concatenation picture. Firefox was the first browser to optimize string concatenation. Beginning with version 1.0, the array technique is actually slower than using the plus operator in all cases. Other browsers have also optimized string concatenation, so Safari, Opera, Chrome, and Internet Explorer 8 also show better performance using the plus operator. Internet Explorer prior to version 8 didn’t have such an optimization, and so the array technique is always faster than the plus operator. — Writing Efficient JavaScript: Chapter 7 – Even Faster Websites 12年5月28日星期⼀一
  • 31. The V8 engine (used in Google Chrome) uses this code to do string concatenation: // ECMA-262, section 15.5.4.6 function StringConcat() {   if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {     throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);   }   var len = %_ArgumentsLength();   var this_as_string = TO_STRING_INLINE(this);   if (len === 1) {     return this_as_string + %_Arguments(0);   }   var parts = new InternalArray(len + 1);   parts[0] = this_as_string;   for (var i = 0; i < len; i++) {     var part = %_Arguments(i);     parts[i + 1] = TO_STRING_INLINE(part);   }   return %StringBuilderConcat(parts, len + 1, ""); } 12年5月28日星期⼀一
  • 32. 性能优化点 avoid using with block 12年5月28日星期⼀一
  • 34. var person = { The with construct introduces an extra scope for name: "Nicholas", the script engine to search through whenever a age: 30 variable is referenced. This alone produces a }; minor performance decrease. However, the contents of that scope are not known at compile function displayInfo(){ time, meaning that the compiler cannot optimize var count = 5; for it, in the same way as it can with normal scopes with(person){ (such as those created by functions). alert(name + " is " + age); alert("Count is " + count); } } displayInfo(); 12年5月28日星期⼀一
  • 35. 性能优化点 cache the compiled template. 12年5月28日星期⼀一
  • 36. Cache The Compiled Template JavaScript, like many scripting languages, allows you to take a string containing code and execute it from within running code. There are four standard ways to accomplish this: eval(), the Function() constructor, setTimeout(), and setInterval(). Each of these functions allows you to pass in a string of JavaScript code and have it executed. - Hign Performance Javascript 12年5月28日星期⼀一
  • 37. 安全性 var json={ ! output:'<script>alert("XSS");</script>' }; document.write $(node).html() juicer('${output}',json); //输出:&lt;script&gt;alert("XSS");&lt;/script&gt; juicer('$${output}',json); //输出:<script>alert("XSS");</script> 12年5月28日星期⼀一
  • 42. {@each list as it, k} <span class=”{@if k+2>list.length}notdot{@/if}”>...</span> {@/each} 12年5月28日星期⼀一
  • 43. http://taobao.com/s?q=%E6%B7%98%E5%AE%9D json={query:’淘宝’,...} http://taobao.com/s?q=${query|encodeURIComponent} 12年5月28日星期⼀一
  • 47. compile render template reusable function html code compiled template 12年5月28日星期⼀一
  • 48. 单一接口 (function() { var juicer = function() { var args = [].slice.call(arguments); args.push(juicer.options); if(arguments.length == 1) { return juicer.compile.apply(juicer, args); } if(arguments.length >= 2) { return juicer.to_html.apply(juicer, args); } }; ... ... 12年5月28日星期⼀一
  • 49. 编译 juicer.compile = function(tpl, options) { if(!options || options !== this.options) { options = __creator(options, this.options); } try { var engine = this.__cache[tpl] ? this.__cache[tpl] : new this.template(this.options).parse(tpl, options); if(!options || options.cache !== false) { this.__cache[tpl] = engine; } return engine; } catch(e) { __throw('Juicer Compile Exception: ' + e.message); return { render: function() {} //noop }; } }; 12年5月28日星期⼀一
  • 50. 编译 this.parse = function(tpl, options) { var _that = this; if(!options || options.loose !== false) { tpl = this.__lexicalAnalyze(tpl) + tpl; } tpl = this.__removeShell(tpl, options); tpl = this.__toNative(tpl, options); this._render = new Function('_, _method', tpl); this.render = function(_, _method) { if(!_method || _method !== that.options._method) { _method = __creator(_method, that.options._method); } return _that._render.call(this, _, _method); }; return this; }; 12年5月28日星期⼀一
  • 51. before __removeShell <ul>     {@each list as it,k}         {@if it.show}             <li>${it.name} (index: ${k})</li>         {@/if}     {@/each} </ul> 12年5月28日星期⼀一
  • 52. after __removeShell <ul>     <% for var i0 = 0; i0 < list; i0++ { %> <% var it = list[i0]; %> <% var k = i0; %>         <% if(it.show) { %>             <li><%= it.name %> (index: <%= k %>)</li>         <% } %>     <% } %> </ul> 12年5月28日星期⼀一
  • 53. __toNative function anonymous(_, _method) { 'use strict'; var _ = _ || {}; var _out = ''; _out += ''; try { _out += ''; var list = _.list; var it = _.it; __lexicalAnalyze var k = _.k; _out += '<ul>'; for (var i0 = 0, li0 = list.length; i0 < li0; i0++) { var it = list[i0]; var k = i0; _out += ''; if (it.show) { _out += '<li>'; _out += _method.__escapehtml.escaping(_method.__escapehtml.detection((it.name))); _out += ' (index: '; _out += _method.__escapehtml.escaping(_method.__escapehtml.detection((k))); _out += ')</li> '; } _out += ''; } _out += '</ul>'; } catch (e) { _method.__throw("Juicer Render Exception: " + e.message); } _out += ''; return _out; } 12年5月28日星期⼀一
  • 54. 渲染函数 this.parse = function(tpl, options) { var _that = this; if(!options || options.loose !== false) { tpl = this.__lexicalAnalyze(tpl) + tpl; } tpl = this.__removeShell(tpl, options); tpl = this.__toNative(tpl, options); this._render = new Function('_, _method', tpl); this.render = function(_, _method) { if(!_method || _method !== that.options._method) { _method = __creator(_method, that.options._method); } return _that._render.call(this, _, _method); }; return this; }; 12年5月28日星期⼀一
  • 55. 参数/方法继承 var __creator = function(o, proto) { o = o !== Object(o) ? {} : o; if(o.__proto__) { o.__proto__ = proto; return o; } var _Empty = function() {}; var n = new((_Empty).prototype = proto, _Empty); for(var i in o) { if(o.hasOwnProperty(i)) { n[i] = o[i]; } } return n; }; 12年5月28日星期⼀一
  • 56. 编译并渲染 juicer.to_html = function(tpl, data, options) { if(!options || options !== this.options) { options = __creator(options, this.options); } return this.compile(tpl, options).render(data, options._method); }; 12年5月28日星期⼀一
  • 58. this.parse = function(tpl, options) { var _that = this; if(!options || options.loose !== false) { tpl = this.__lexicalAnalyze(tpl) + tpl; } tpl = this.__removeShell(tpl, options); tpl = this.__toNative(tpl, options); this._render = new Function('_, _method', tpl); this.render = function(_, _method) { var fn = function() {}; fn.toString = function() { if(!_method || _method !== that.options._method) { _method = __creator(_method, that.options._method); } return _that._render.call(this, _, _method); }; return fn; }; return this; }; 12年5月28日星期⼀一
  • 59. 展望 ·继续优化 Juicer (性能、源代码) ·完善更多的文档和实利 ·提供其它模板语法到 Juicer 模板的自动化工具 ·更多的语言支持(诸如PHP、Python、Ruby …) ·精确的模板调试模式,精确到行号和模板错误位置 ·持续改进 12年5月28日星期⼀一
  • 60. Q&A juicer(‘${over}’, {over:‘thanks!’}); 12年5月28日星期⼀一