javascript 模板系統 ejs v1

來源:互聯網
上載者:User

由於各種原因,被逼使用前台模板。看了一下其他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;            })();

<br /><!doctype html><br /><html><br /> <head><br /> <meta charset="utf-8"/><br /> <meta content="IE=8" http-equiv="X-UA-Compatible"/><br /> <meta name="keywords" content="javascript模板 by 司徒正美" /><br /> <meta name="description" content="javascript模板 by 司徒正美" /><br /> <title>javascript模板 by 司徒正美</title><br /> </head><br /> <body><br /> <h1>javascript模板 by 司徒正美</h1><br /> <script id="tmpl" type="text/html"><br /> <h2><%= name %></h2><br /> <%# 這是注釋!!!!!!!!! %><br /> <ul><br /> <% for(var i=0; i< supplies.length; i++){ %><br /> <li><%= supplies[i] %></li><br /> <% } %><br /> </ul><br /> <% var color = "color:red;" %><br /> <p style="text-indent:2em;<%= color %>"><%= address %></p><br /> </script></p><p> <script></p><p> (function () {<br /> if(!String.prototype.trim){<br /> String.prototype.trim = function(str) {<br /> return this.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');<br /> }<br /> }<br /> var dom = {<br /> quote: function (str) {<br /> str = str.replace(/[\x00-\x1f\\]/g, function (chr) {<br /> var special = metaObject[chr];<br /> return special ? special : '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4)<br /> });<br /> return '"' + str.replace(/"/g, '\\"') + '"';<br /> }<br /> },<br /> metaObject = {<br /> '\b': '\\b',<br /> '\t': '\\t',<br /> '\n': '\\n',<br /> '\f': '\\f',<br /> '\r': '\\r',<br /> '\\': '\\\\'<br /> },<br /> parser = document.createElement("div"),<br /> startOfHTML = "\t__views.push(",<br /> endOfHTML = ");\n",<br /> outerScan = function(str,buff,left,right){<br /> var index = str.indexOf(left);<br /> if(index !== -1){<br /> buff.push(startOfHTML, dom.quote(str.slice(0,index)), endOfHTML);<br /> innerScan(str.slice(index+2),buff,left,right);<br /> }else{<br /> buff.push(startOfHTML, dom.quote(str), endOfHTML);<br /> }<br /> },<br /> innerScan = function(str,buff,left,right){<br /> var index = str.indexOf(right);<br /> if(index !== -1){<br /> var text = str.slice(0,index);<br /> switch (text.charAt(0)) {<br /> case "#"://處理注釋<br /> break;<br /> case "="://處理後台返回的變數(輸出到頁面的)<br /> buff.push(startOfHTML, text.slice(1), endOfHTML)<br /> break;<br /> default:<br /> buff.push(text, "\n")<br /> }<br /> outerScan( str.slice(index+2),buff,left,right);<br /> }else{<br /> throw "找不到右界定符 " + str<br /> }<br /> }</p><p> //onsite,可選,Boolean,是否就地替換掉模板容器,預設true,如果為false,則返回一個文檔片段,交由使用者自己插入到需要的地方<br /> dom.ejs = function (obj) {<br /> var onsite = obj.onsite === void 0 ,<br /> left = obj.left || "<%",<br /> right = obj.right || "%>",<br /> selector = obj.selector,<br /> buff = ["var __views = [];\n"],<br /> fragment = document.createDocumentFragment(),<br /> el = document.getElementById(selector),<br /> ejs = dom.ejs;<br /> if (!el) throw "找不到目標元素";<br /> if(!ejs[selector]){<br /> outerScan(el.text.trim(),buff,left,right);<br /> ejs[selector] = new Function("json", "with(json){"+buff.join("") + '};return __views.join("");')<br /> }<br /> parser.innerHTML = ejs[selector](obj.json || {});<br /> while (parser.firstChild) {<br /> fragment.appendChild(parser.firstChild)<br /> }<br /> return onsite ? el.parentNode.replaceChild(fragment, el) : fragment;<br /> };<br /> window.dom = dom;</p><p> })();</p><p> window.onload = function(){<br /> dom.ejs({<br /> selector:"tmpl",<br /> json: {<br /> name:"司徒正美",<br /> supplies:["第一個LI元素","第二個LI元素","第三個LI元素","第四個LI元素"],<br /> address:"異次元"}<br /> });<br /> }<br /> </script><br /> </body><br /></html><br />

運行代碼

PS:發現javascript模板沒有想象中的糟糕,尤其是大流量的頁面在無法使用UI庫的情況下,這是個不錯的選擇,例子如qq zone與ニコニコ動畫。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.