簡介

  前段時間,想著自己寫一個簡單的模版引擎,便于自己平時開發(fā)demo時使用,同時也算是之前學(xué)習(xí)的知識的一種總結(jié)吧!

  首先我們先了解一下模版引擎的工作原理吧!

  1. 模版引擎其實就是將指定標(biāo)簽的內(nèi)容根據(jù)固定規(guī)則,解析為可執(zhí)行語句字符串;

  2. 執(zhí)行可執(zhí)行解析后的語句字符串,即生成我們想要的頁面結(jié)構(gòu)。

具體實現(xiàn)方法:

1. 最終效果

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

 1     /*  解析前  2             <ul>  3                 {{for(var i = 0; i < data.todos.length; ++i)}}  4                     {{if(data.todos[i].todo_type)}}  5                         <li>{{data.todos[i].todo_name}}</li>  6                     {{/if}}  7                 {{/for}}  8             </ul>  9      */ 10      11     /*  解析后 12         var str = ""; 13         str += "<ul>"; 14         for (var i = 0; i < data.todos.length; ++i) { 15             if (data.todos[i].todo_type) { 16                 str += "<li>"; 17                 str += data.todos[i].todo_name; 18                 str += "</li>"; 19             } 20         } 21         str += "</ul>"; 22      */ 23      24     /*  執(zhí)行后 25         <ul><li>eat</li><li>sleep</li><li>play</li></ul> 26      */

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

2.  整體分析 

  1. 定義屬于自己的模版引擎格式

  2. 創(chuàng)建一個全局對象,它包括存放編譯后字符串的屬性,編譯和執(zhí)行的函數(shù)方法,以及一些工具函數(shù)

3. 具體實現(xiàn)

  1. 自定義模版引擎格式

    1. 賦值 {{data}}
        2. 判斷 {{if(...) { }} {{ } else if(...) { }} {{ } else { }} {{ } }}
        3. 對象 {{for(key in object) { }} {{ } }}
        4. 數(shù)組 {{for(var i = 0); i < arrays.length; ++i) { }} {{ } }}
        處理賦值以外,其他語句需要獨占一行

  2. 定義全局對象

  全局對象中包括五個函數(shù)和一個字符串:其中complileTpl用于解析字符串,executeTpl用于運行解析生成的代碼, jsStr用于存放解析生成的字符串,其他都是中間處理函數(shù)。

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

var template = {     // 存放解析后的js字符串     jsStr: "var str = '';",     /**      * 將模版中的字符串解析為可執(zhí)行的js語句      * @param  {string} tpl 模版字符串      */     complileTpl: function(tpl) {     },     /**      * 執(zhí)行解析后的js語句      * @param  {DOM對象}  root    掛載對象      * @param  {json}     data   解析的數(shù)據(jù)對象      */     executeTpl: function(root, data) {     },     /**      * 不包含指令行的處理函數(shù)      * @param  {string} str 需要處理的字符串      */     _handleLabel: function(str) {     },     /**      * 包含指令行的處理函數(shù)      * @param  {string} str 需要處理的字符串      */     _handleDirective: function(str) {     },     /**      * 處理字符串前后空白      * @param  {string} str 需要處理的字符串      */     _handlePadding: function(str) {     } }

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

 

  3. 解析函數(shù)詳解

  由于我是在mac上開發(fā)的,mac上'\n'表示換行。

  首先根據(jù)換行符,將標(biāo)簽中的字符串,分隔為數(shù)組。然后分別根據(jù)每一行中是否包含指令,進(jìn)行不同的處理。

  如果不包含指令,創(chuàng)建一個將該字符串添加到存儲字符串的變量jsStr中。

  如果包含指令,由于我設(shè)置了格式要求,只有賦值操作可以和html標(biāo)簽在同一行,其他的指令都要獨占一樣,所以,當(dāng)為賦值情況下,將指令左右的標(biāo)簽元素作為字符串操作,添加到變量jsStr中,如過是其他指令,直接去掉{{}},添加到變量jsStr即可。

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

    /**      * 將模版中的字符串解析為可執(zhí)行的js語句      * @param  {string} tpl 模版字符串      */     complileTpl: function(tpl) {         // 模版字符串按行分隔         var tplArrs = tpl.split('\n');         for (var index = 0; index < tplArrs.length; ++index) {             var item = this._handlePadding(tplArrs[index]);             // 處理不包含指令的行             if (item.indexOf('{{') == -1) {                 this._handleLabel(item);             } else {                 this._handleDirective(item);             }         }     },     /**      * 不包含指令行的處理函數(shù)      * @param  {string} str 需要處理的字符串      */     _handleLabel: function(str) {         // 去除空行或者空白行         if (str) {             this.jsStr += "str += '" + str + "';";         }     },     /**      * 包含指令行的處理函數(shù)      * @param  {string} str 需要處理的字符串      */     _handleDirective: function(str) {         // 處理指令前的字符串         var index = str.indexOf('{{');         var lastIndex = str.lastIndexOf('}}');         if (index == 0 && lastIndex == str.length - 2) {             this.jsStr += str.slice(index + 2, lastIndex);         } else if (index != 0 && lastIndex != str.length - 2) {             this.jsStr += "str += '" + str.slice(0, index) + "';";             this.jsStr += "str += " + str.slice(index + 2, lastIndex) + ";";             this.jsStr += "str += '" + str.slice(lastIndex + 2, str.length) + "';";         } else {             throw new Error('格式錯誤');         }     },    

    /**
     * 處理字符串前后空白
     * @param  {string} str 需要處理的字符串
     */
    _handlePadding: function(str) {
        return str.replace(/^\s*||\s*$/g, '');
    }

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

  4. 執(zhí)行編譯后的字符串語句

  使用eval運行編譯后的字符串語句。

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

    /**      * 執(zhí)行解析后的js語句      * @param  {DOM對象}  root    掛載對象      * @param  {json}     data   解析的數(shù)據(jù)對象      */     executeTpl: function(root, data) {         var html = eval(this.jsStr);         console.log(html);         root.innerHTML = html;     },

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

  5. 使用方法

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

 1 <!DOCTYPE html>  2 <html>  3 <head>  4     <meta charset="UTF-8">  5     <title>Document</title>  6     <script src="utils/template.js"></script>  7 </head>  8 <body>  9 <div id="test"> 10      11 </div> 12     <script id="test_template" type="text/my_template"> 13         <ul> 14             {{for(var i = 0; i < data.todos.length; ++i) { }} 15                 {{if(data.todos[i].todo_type) { }} 16                     <li>{{data.todos[i].todo_name}}</li> 17                 {{ } }} 18             {{ } }} 19         </ul> 20     </script> 21  22     <script> 23         var data = { 24             todos: [{ 25                 todo_name: "eat", 26                 todo_type: "todo" 27             }, { 28                 todo_name: "sleep", 29                 todo_type: "completed" 30             }, { 31                 todo_name: "play", 32                 todo_type: "todo" 33             }] 34          35         }; 36         var tpl = document.getElementById('test_template'); 37  38         str = tpl.innerHTML; 39  40         template.complileTpl(str); 41  42         var root = document.getElementById('test'); 43  44         template.executeTpl(root, data); 45     </script> 46 </body> 47 </html>

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

4. 延伸

  eval等價于evil!

  為什么呢?各大js權(quán)威書籍上都不提倡使用eval。下面我詳細(xì)的解釋一下為什么不提倡。

  首先,大家需要知道,js并不是一門解釋型語言。它和其他大家熟知的編程語言(c,java,c++)一樣,是編譯型語言。但是,它和其他的編譯型語言又不完全一樣。眾所周知,C語言等是預(yù)編譯的語言,它們可以編譯成目標(biāo)代碼,移植到其他機(jī)器中運行。而js呢,它并不是一門預(yù)編譯的語言,它的編譯過程可能只在執(zhí)行前一秒。但是,它確實在執(zhí)行前進(jìn)行了編譯過程。

  然后,大家要了解一下,詞法作用域。所謂的詞法作用域,是指當(dāng)前作用域,可以訪問的變量。

  js編譯過程,其實就是在將申明的變量添加當(dāng)前詞法作用域,并將其他代碼編譯成可執(zhí)行代碼。然而,在瀏覽器中,做了一些列的優(yōu)化,可以通過靜態(tài)代碼分析,定位申明的變量和函數(shù)的位置,方便后續(xù)訪問。然而,我們卻可以通過eval函數(shù),改變當(dāng)前詞法作用域。這樣一樣,瀏覽器所做的優(yōu)化都將付諸一炬。當(dāng)出現(xiàn)eval,瀏覽器做的最好的處理方式,就是不做任何處理。

  以上為為什么不提倡使用eval,下面我是如何規(guī)避eval函數(shù)!

  主要的思路是:我們經(jīng)常使用script標(biāo)簽動態(tài)添加腳本文件,同樣我們也可以通過script標(biāo)簽中添加可執(zhí)行語句字符串,也就可以動態(tài)添加可執(zhí)行語句。

代碼如下:

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

 1 /**  2      * 將傳入的可執(zhí)行字符串,通過script標(biāo)簽執(zhí)行   3      * @param  {[string]} str 可執(zhí)行字符串  4      */  5     function strToFun(str) {  6         // 創(chuàng)建script標(biāo)簽  7         var script = document.createElement('script');  8         script.id = 'executableString';  9  10         // 處理傳入的字符串,當(dāng)相應(yīng)的語句執(zhí)行完畢后,將script標(biāo)簽移除 11         var handleStr = '(function() { ' + str + ';var script = document.getElementById("executableString"); document.body.removeChild(script); })();';  12  13         // 將待執(zhí)行的代碼添加到剛創(chuàng)建的script標(biāo)簽中 14         script.innerHTML = handleStr; 15  16            // 將創(chuàng)建的腳本追加到DOM樹中 17         document.body.appendChild(script); 18     }

seo優(yōu)化培訓(xùn),網(wǎng)絡(luò)推廣培訓(xùn),網(wǎng)絡(luò)營銷培訓(xùn),SEM培訓(xùn),網(wǎng)絡(luò)優(yōu)化,在線營銷培訓(xùn)

  以上,只是我一時的想法,希望大家積極提供不同的想法?。?!

  雖然上面在解決eval問題的同時,引入了DOM操作,可能沒有改善性能,但是,這種方法是可以解決CSP(Content-Security-Policy)問題!?。–SP中可能會禁止使用eval函數(shù))。  

http://www.cnblogs.com/diligentYe/p/7076720.html