javascript 模板系统 ejs v1

本文探讨了在网页中创建自定义JavaScript模板引擎的过程,包括模板存放位置、样式选择及其实现细节,以及如何利用模板生成动态内容。

由于各种原因,被逼使用前台模板。看了一下其他JS模板库的实现,发现其原理并不难,遂决定重造轮子。

做一个前台模板,有如下几个问题需要考量:

  • 模板是放置于哪里?是内嵌于HTML页面还是像JS文件那样独立出来?如果是内嵌可以减少请求数但无法让模板重用于另一个HTML页面,反之亦然。
  • 如果是内嵌于HTML页面,如何存放它?目前有两种方式,script标签与textarea。
  • 模板界定符的风格,是ASP的<%与%>,还是Django的{{与}},还是其他方式。

下面是我一些不成熟的见解:

  • 应该存在两种模板,普通模板(内嵌于HTML)与局部模板(存放于独立的文件中)。普通模板是为某个页面订制的,局部模板可以是订制的,但多是为了实现多页面的重用,它可以与普通模板一起组成完整的模板。用rails的术语来说,这是一个partial。
  • 普通模板的容器为一个丧失解析脚本能力的script标签,因为我们可以不需要在script标签里面内嵌script标签。但是与textara作为容器,就很可能碰到模板存在textarea的情况,这时就存在一个错位套嵌的问题。
  • 模板风格,让它可以定制就行了。默认是ASP风格,好让它在一些主流的IDE中自动排版。

我把我的模板引擎称之为ejs(embedded javascript snippet,嵌入式javascript代码片断)。任何javascript模板只最终目的就是生成一个可以传入后台参数的函数。

<!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>
        <script id="tmpl" type="text/html">
                <h2><%=  name %></h2>
                <%# 这是注释!!!!!!!!! %>
                <ul>
                <% for(var i=0; i< supplies.length; i++){ %>
                    <li><%=  supplies[i] %></li>
                    <% } %>
                </ul>
                <% var color = "color:red;" %>
                <p style="text-indent:2em;<%= color %>"><%= address %></p>
        </script>
         <script id="tmpl" type="template" ></script  >
        <script>  

            window.onload = function(){
                dom.ejs({
                    selector:"tmpl",
                    json: {
                        name:"司徒正美",
                        supplies:["第一个LI元素","第二个LI元素","第三个LI元素","第四个LI元素"],
                        address:"异次元"}
                });
            }
        </script>
    </body>
</html>

模板系统:

 //司徒正美 javascript template - http://www.cnblogs.com/rubylouvre/ - MIT Licensed
            (function () {
                if(!String.prototype.trim){
                    String.prototype.trim = function(str) {
                        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',
                    '\\': '\\\\'
                },
                parser = document.createElement("div"),
                startOfHTML = "\t__views.push(",
                endOfHTML = ");\n",
                outerScan = function(str,buff,left,right){
                    var index = str.indexOf(left);
                    if(index !== -1){
                        buff.push(startOfHTML, dom.quote(str.slice(0,index)), endOfHTML);
                        innerScan(str.slice(index+2),buff,left,right);
                    }else{
                        buff.push(startOfHTML, dom.quote(str), endOfHTML);
                    }
                },
                innerScan = function(str,buff,left,right){
                    var index = str.indexOf(right);
                    if(index !== -1){
                        var text = str.slice(0,index);
                        switch (text.charAt(0)) {
                            case "#"://处理注释
                                break;
                            case "="://处理后台返回的变量(输出到页面的)
                                buff.push(startOfHTML, text.slice(1), endOfHTML)
                                break;
                            default:
                                buff.push(text, "\n")
                        }
                        outerScan( str.slice(index+2),buff,left,right);
                    }else{
                        throw "找不到右界定符 " + str
                    }
                }

                //onsite,可选,Boolean,是否就地替换掉模板容器,默认true,如果为false,则返回一个文档碎片,交由用户自己插入到需要的地方
                dom.ejs = function (obj) {
                    var onsite = obj.onsite === void 0 ,
                    left = obj.left || "<%",
                    right = obj.right || "%>",
                    selector = obj.selector,
                    buff = ["var __views = [];\n"],
                    fragment = document.createDocumentFragment(),
                    el = document.getElementById(selector),
                    ejs = dom.ejs;
                    if (!el) throw "找不到目标元素";
                    if(!ejs[selector]){
                        outerScan(el.text.trim(),buff,left,right);
                        ejs[selector] = new Function("json", "with(json){"+buff.join("") + '};return __views.join("");')
                    }
                    parser.innerHTML = ejs[selector](obj.json || {});
                    while (parser.firstChild) {
                        fragment.appendChild(parser.firstChild)
                    }
                    return onsite ? el.parentNode.replaceChild(fragment, el) : fragment;
                };
                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> <script id="tmpl" type="text/html"> <h2><%= name %></h2> <%# 这是注释!!!!!!!!! %> <ul> <% for(var i=0; i< supplies.length; i++){ %> <li><%= supplies[i] %></li> <% } %> </ul> <% var color = "color:red;" %> <p style="text-indent:2em;<%= color %>"><%= address %></p> </script> <script> (function () { if(!String.prototype.trim){ String.prototype.trim = function(str) { 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', '\\': '\\\\' }, parser = document.createElement("div"), startOfHTML = "\t__views.push(", endOfHTML = ");\n", outerScan = function(str,buff,left,right){ var index = str.indexOf(left); if(index !== -1){ buff.push(startOfHTML, dom.quote(str.slice(0,index)), endOfHTML); innerScan(str.slice(index+2),buff,left,right); }else{ buff.push(startOfHTML, dom.quote(str), endOfHTML); } }, innerScan = function(str,buff,left,right){ var index = str.indexOf(right); if(index !== -1){ var text = str.slice(0,index); switch (text.charAt(0)) { case "#"://处理注释 break; case "="://处理后台返回的变量(输出到页面的) buff.push(startOfHTML, text.slice(1), endOfHTML) break; default: buff.push(text, "\n") } outerScan( str.slice(index+2),buff,left,right); }else{ throw "找不到右界定符 " + str } } //onsite,可选,Boolean,是否就地替换掉模板容器,默认true,如果为false,则返回一个文档碎片,交由用户自己插入到需要的地方 dom.ejs = function (obj) { var onsite = obj.onsite === void 0 , left = obj.left || "<%", right = obj.right || "%>", selector = obj.selector, buff = ["var __views = [];\n"], fragment = document.createDocumentFragment(), el = document.getElementById(selector), ejs = dom.ejs; if (!el) throw "找不到目标元素"; if(!ejs[selector]){ outerScan(el.text.trim(),buff,left,right); ejs[selector] = new Function("json", "with(json){"+buff.join("") + '};return __views.join("");') } parser.innerHTML = ejs[selector](obj.json || {}); while (parser.firstChild) { fragment.appendChild(parser.firstChild) } return onsite ? el.parentNode.replaceChild(fragment, el) : fragment; }; window.dom = dom; })(); window.onload = function(){ dom.ejs({ selector:"tmpl", json: { name:"司徒正美", supplies:["第一个LI元素","第二个LI元素","第三个LI元素","第四个LI元素"], address:"异次元"} }); } </script> </body> </html>

运行代码

PS:发现javascript模板没有想象中的糟糕,尤其是大流量的页面在无法使用UI库的情况下,这是个不错的选择,例子如qq zoneニコニコ動画

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值