javascript模板系统 ejs v3

本版本增添了局部模板功能,并且允许主模板调用局部模板,局部模块调用局部模块,并去掉onsite变量,不再提供解析成毕的文档碎片给使用者。它使用双重缓存,一是缓存那些通过同步请求得到的文本而成的数组,一是整体解析完毕得到的模板函数。模板函数是通过数组元素拼凑动成解析而成的,这是大大提高了效率。不过由于新功能的加入,虽然动用了新的构筑算法也比不上v2的构筑速度了……

有人说不要使用<%与%>做界定符,这个问题我在v1版本已经提出过了,这些都是可以自定义的。本文的例子将演示一下如何使用Django的{{与}}风格。

最后隆重推介一下本版本的新功能。不过由于条件有限,无法演示。现在模板不单单是内嵌于页面的script标签之内,也可以放置在一个独立的文件之内,如html,ejs,text,随你起什么后缀名。这个文件将会用同步请求回来用于构筑模板函数。我们可以用使用url属性,或在模板中使用<%: /template/partail.ejs >实现。一般而言,url是用于主模板,而<%: url %>是用于局部模板。如果我们在配置对象中同时使用selector与url,selector的优先级是高于url的。


   var data = dom.ejs({

                    selector:"tmpl",

                    url:"/template/aaa.html",

                    left:"{{",

                    right:"}}",

                    json: {

                        name:"司徒正美",

                        blog:"ruby louvre"

                        address:"异次元"

                    }

     });

源码:


//司徒正美 javascript template - http://www.cnblogs.com/rubylouvre/ - MIT Licensed



            (function () {



                if(!String.prototype.trim){

                    String.prototype.trim = function(){

                        return this.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');

                    }

                }

                var dom = {

                    quote: function (str) {

                        str = str.replace(/[\x00-\x1f\\]/g, function (chr) {

                            var special = metaObject[chr];

                            return special ? special : '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4)

                        });

                        return '"' + str.replace(/"/g, '\\"') + '"';

                    }

                },

                metaObject = {

                    '\b': '\\b',

                    '\t': '\\t',

                    '\n': '\\n',

                    '\f': '\\f',

                    '\r': '\\r',

                    '\\': '\\\\'

                },

                startOfHTML = "\t__views.push(",

                endOfHTML = ");\n";

                (function(){

                    //http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx

                    var s = ["XMLHttpRequest",

                        "ActiveXObject('Msxml2.XMLHTTP.6.0')",

                        "ActiveXObject('Msxml2.XMLHTTP.3.0')",

                        "ActiveXObject('Msxml2.XMLHTTP')",

                        "ActiveXObject('Microsoft.XMLHTTP')"];

                    if( eval("''+/*@cc_on"+" @_jscript_version@*/-0")*1 === 5.7 && location.protocol === "file:"){

                        s.shift();

                    }

                    for(var i = 0 ,el;el=s[i++];){

                        try{

                            if(eval("new "+el)){

                                dom.xhr = new Function( "return new "+el)

                                break;

                            }

                        }catch(e){}

                    }

                })();



                dom.partial = function(url){

                    var xhr = dom.xhr();

                    xhr.open("GET",url,false);

                    xhr.setRequestHeader("If-Modified-Since","0");

                    xhr.send(null);

                    return xhr.responseText|| ""

                }



                dom.tmpl = function(str,rLeft,rRight,sRight){

                    var arr = str.trim().split(rLeft),self = arguments.callee,buff = [],url,els,el,i = 0, n= arr.length;

                    while (i<n) {

                        els = arr[i++]; el = els.split(rRight);

                        if(els.indexOf(sRight) !== -1){//这里不使用els.length === 2是为了避开IE的split bug

                            switch (el[0].charAt(0)) {

                                case "#"://处理注释

                                    break;

                                case "="://处理后台返回的变量(输出到页面的);

                                    buff.push(startOfHTML, el[0].substring(1), endOfHTML)

                                    break;

                                case ":"://处理局部模板

                                    url = el[0].substring(1).trim();

                                    //缓存构筑函数的数组

                                    self[url] = self[url] || self.call(null,dom.partial(url),rLeft,rRight,sRight);

                                    buff = buff.concat(dom.tmpl[url] );

                                    break;

                                default:

                                    buff.push(el[0], "\n");

                            };

                            el[1] &&  buff.push(startOfHTML, dom.quote.call(null,el[1]), endOfHTML);

                        }else{

                            buff.push(startOfHTML, dom.quote.call(null,el[0]), endOfHTML);

                        }

                    }

                    return buff;

                }



                dom.ejs = function (obj) {

                    var sLeft = obj.left || "%>",

                    sRight = obj.right || "<%",

                    rLeft = new RegExp("\\s*"+sLeft+"\\s*"),

                    rRight = new RegExp("\\s*"+sRight+"\\s*"),

                    buff = ["var __views = [];\n"],

                    key = obj.selector || obj.url,str;

                    if(obj.selector){

                        var el = document.getElementById(key);

                        if (!el) throw "找不到目标元素";

                        str = el.text;

                    }else{

                        str =  dom.partial(key);

                        if(!str) throw "目标文件不存在";

                    }

                    if(!dom.tmpl[key]){//缓存模板函数

                        buff = buff.concat(dom.tmpl.call(null,str,rLeft,rRight,sRight));

                        dom.tmpl[key] = new Function("json", "with(json){"+buff.join("") + '\t};return __views.join("");');

                    }

                    return dom.tmpl[key](obj.json || {});

                };

                window.dom = dom;



            })();

示例:


<!doctype html>

<html>

    <head>

        <meta charset="utf-8"/>

        <meta content="IE=8" http-equiv="X-UA-Compatible"/>

        <meta name="keywords" content="javascript模板 by 司徒正美" />

        <meta name="description" content="javascript模板 by 司徒正美" />

        <title>javascript模板 by 司徒正美</title>

    </head>

    <body>

        <h1>javascript模板 by 司徒正美</h1>

        <div id="tmplTC">这是容器</div>

        <script id="tmpl" type="tmpl">

                <h2>{{=  name }}{{= name }}</h2>

                {{# 这是注释!!!!!!!!! }}

                <ul>

                {{ for(var i=0; i< uls.length; i++){ }}

                    <li>{{=  uls[i] }}的名字是{{= name }}</li>

                    {{  }  }}

                </ul>

                {{ var color = "color:red;" }}

                <p style="text-indent:2em;{{= color }} ">{{= address }}</p>

           

        </script>

       <script src="dom/ejs.js"></script>

        <script>

            window.onload = function(){

                var els = [];

                for(var i=0;i<1000;i++){

                    els.push("第"+i+"个元素")

                }

                var a = new Date

                var data = dom.ejs({

                    selector:"tmpl",

                    left:"{{",

                    right:"}}",

                    json: {

                        name:"司徒正美",

                        uls:els,

                        address:"异次元"

                    }

                });

                document.getElementById("tmplTC").innerHTML = data;

                alert( new Date-a)

            }

        </script>

    </body>

</html>

现在我有一个考量,就是随着模板规模的膨胀,里面可能夹杂着越来越多变量,我们就很难分辨得清那些后台传过的东西,那些是本地的临时变量,后台的需求一变更,后台的json数据也就要改动。这维护起来非常困难。因此我非常欣赏ruby的变量书写风格,从变量名就知它是实例变量,类变量,普通变量与常量,它还有符号这东西呢,我会v4版本加入重新加入@标识符的。

你可能感兴趣的:(JavaScript)