亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > JS > 正文

JavaScript模板引擎實現原理實例詳解

2024-05-06 16:47:07
字體:
來源:轉載
供稿:網友

本文實例講述了JavaScript模板引擎實現原理。分享給大家供大家參考,具體如下:

1、入門實例

首先我們來看一個簡單模板:

 <script type="template" id="template">  <h2>   <a href="{{href}}" rel="external nofollow" >    {{title}}   </a>  </h2>  <img src="{{imgSrc}}" </script>

其中被{{ xxx }}包含的就是我們要替換的變量。

接著我們可能通過ajax或者其他方法獲得數據。這里我們自己定義了數據,具體如下:

var data = [  {   title: "Create a Sticky Note Effect in 5 Easy Steps with CSS3 and HTML5",   href: "http://net.tutsplus.com/tutorials/html-css-techniques/create-a-sticky-note-effect-in-5-easy-steps-with-css3-and-html5/",   imgSrc: "https://d2o0t5hpnwv4c1.cloudfront.net/771_sticky/sticky_notes.jpg"  },  {   title: "Nettuts+ Quiz #8",   href: "http://net.tutsplus.com/articles/quizzes/nettuts-quiz-8-abbreviations-darth-sidious-edition/",   imgSrc: "https://d2o0t5hpnwv4c1.cloudfront.net/989_quiz2jquerybasics/quiz.jpg"  } ];

ok,現在的問題就是我們怎么把數據導入到模板里面呢?

第一種大家會想到的就是采用replace直接替換里面的變量:

template = document.querySelector('#template').innerHTML,result = document.querySelector('.result'),i = 0, len = data.length,fragment = '';for ( ; i < len; i++ ) {  fragment += template   .replace( //{/{title/}/}/, data[i].title )   .replace( //{/{href/}/}/, data[i].href )   .replace( //{/{imgSrc/}/}/, data[i].imgSrc );}result.innerHTML = fragment;

第二種的話,相對第一種比較靈活,采用的是正則替換,對于初級前端,很多人對正則掌握的并不是很好,一般也用的比較少。具體實現如下:

template = document.querySelector('#template').innerHTML,result = document.querySelector('.result'),attachTemplateToData;// 將模板和數據作為參數,通過數據里所有的項將值替換到模板的標簽上(注意不是遍歷模板標簽,因為標簽可能不在數據里存在)。attachTemplateToData = function(template, data) {    var i = 0,      len = data.length,      fragment = '';    // 遍歷數據集合里的每一個項,做相應的替換    function replace(obj) {      var t, key, reg;              //遍歷該數據項下所有的屬性,將該屬性作為key值來查找標簽,然后替換      for (key in obj) {        reg = new RegExp('{{' + key + '}}', 'ig');        t = (t || template).replace(reg, obj[key]);      }      return t;    }    for (; i < len; i++) {      fragment += replace(data[i]);    }    return fragment;  };result.innerHTML = attachTemplateToData(template, data);

與第一種相比較,第二種代碼看上去多了,但是功能實則更為強大了。第一種我們需要每次重新編寫變量名,如果變量名比較多的話,會比較麻煩,且容易出錯。第二種的就沒有這些煩惱。

2、模板引擎相關知識

通過上面的例子,大家對模板引擎應該有個初步的認識了,下面我們來講解一些相關知識。

2.1 模板存放

模板一般都是放置到 textarea/input 等表單控件,或者 script 等標簽中。比如上面的例子,我們就是放在 script 標簽上的。

2.2 模板獲取

一般都是通過ID來獲取,document.getElementById("ID"):

//textarea或input則取value,其它情況取innerHTMLvar html = /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML;

上面的是通用的模板獲取方法,這樣不管你是放在 textarea/input 還是 script 標簽下都可以獲取到。

2.3 模板函數

一般都是templateFun("id", data);其中id為存放模板字符串的元素id,data為需要裝載的數據。

2.4 模板解析編譯

模板解析主要是指將模板中 JavaScript 語句和 html 分離出來,編譯的話將模板字符串編譯成最終的模板。上面的例子比較簡單,還沒有涉及到模板引擎的核心。

2.5 模板分隔符

要指出的是,不同的模板引擎所用的分隔符可能是不一樣,上面的例子用的是{{ }},而Jquery tmpl 使用的是<%  %>。

3、jQuery tmpl 實現原理解析

jQuery tmpl是由jQuery的作者寫的,代碼短小精悍??偣?0多行,功能卻比我們上面的強大很多。我們先來看一看源碼:

(function(){ var cache = {}; this.tmpl = function tmpl(str, data){  var fn = !//W/.test(str) ?   cache[str] = cache[str] ||    tmpl(document.getElementById(str).innerHTML) :   new Function("obj",    "var p=[],print=function(){p.push.apply(p,arguments);};" +    "with(obj){p.push('" +    str     .replace(/[/r/t/n]/g, " ")     .split("<%").join("/t")     .replace(/((^|%>)[^/t]*)'/g, "$1/r")     .replace(//t=(.*?)%>/g, "',$1,'")     .split("/t").join("');")     .split("%>").join("p.push('")     .split("/r").join("//'")   + "');}return p.join('');");  return data ? fn( data ) : fn; };})();

初看是不是覺得有點懵,完全不能理解的代碼。沒事,后面我們會對源碼進行解釋的,我們還是先看一下所用的模板

 <ul>  <% for ( var i = 0; i < users.length; i++ ) { %>     <li><a href="<%=users[i].url%>" rel="external nofollow" ><%=users[i].name%></a></li>  <% } %> </ul>

可以發現,這個模板比入門例子的模板更為復雜,因為里面還夾雜著 JavaScript 代碼。JavaScript 代碼采用 <%  %> 包含。而要替換的變量則是用 <%=   %> 分隔開的。

下面我再來對代碼做個注釋。不過即使看了注釋,你也不一定能很快理解,最好的辦法是自己實際動手操作一遍。

// 代碼整個放在一個立即執行函數里面(function(){ // 用來緩存,有時候一個模板要用多次,這時候,我們直接用緩存就會很方便 var cache = {}; // tmpl綁定在this上,這里的this值得是window this.tmpl = function tmpl(str, data){  // 只有模板才有非字母數字字符,用來判斷傳入的是模板id還是模板字符串,  // 如果是id的話,判斷是否有緩存,沒有緩存的話調用tmpl;  // 如果是模板的話,就調用new Function()解析編譯  var fn = !//W/.test(str) ?   cache[str] = cache[str] ||    tmpl(document.getElementById(str).innerHTML) :   new Function("obj",     // 注意這里整個是字符串,通過 + 號拼接    "var p=[],print=function(){p.push.apply(p,arguments);};" +    "with(obj){p.push('" +    str      // 去除換行制表符/t/n/r     .replace(/[/r/t/n]/g, " ")            // 將左分隔符變成 /t     .split("<%").join("/t")            // 去掉模板中單引號的干擾     .replace(/((^|%>)[^/t]*)'/g, "$1/r")            // 為 html 中的變量變成 ",xxx," 的形式, 如:/t=users[i].url%> 變成 ',users[i].url,'      // 注意這里只有一個單引號,還不配對     .replace(//t=(.*?)%>/g, "',$1,'")            // 這時候,只有JavaScript 語句前面才有 "/t", 將 /t 變成  ');      // 這樣就可把 html 標簽添加到數組p中,而javascript 語句 不需要 push 到里面?!     ?split("/t").join("');")            // 這時候,只有JavaScript 語句后面才有 "%>", 將 %> 變成 p.push('      // 上一步我們再 html 標簽后加了 ');, 所以要把 p.push(' 語句放在 html 標簽放在前面,這樣就可以變成 JavaScript 語句     .split("%>").join("p.push('")      // 將上面可能出現的干擾的單引號進行轉義       .split("/r").join("//'")    // 將數組 p 變成字符串。   + "');}return p.join('');");  return data ? fn( data ) : fn; };})();

上面代碼中,有一個要指出的就是new Function 的使用 方法。給 new Function() 傳一個字符串作為函數的body來構造一個 JavaScript函數。編程中并不經常用到,但有時候應該是很有用的。

下面是 new Function 的基本用法:

// 最后一個參數是函數的 body(函數體),類型為 string;// 前面的參數都是 索要構造的函數的參數(名字)var myFunction = new Function('users', 'salary', 'return users * salary');

最后的字符串就是下面這種形式:

 var p = [],  print = function() {   p.push.apply(p, arguments);  }; with(obj) {  p.push('   <ul>   ');  for (var i = 0; i < users.length; i++) {   p.push('     <li><a href="', users[i].url, '" rel="external nofollow" >', users[i].name, '</a></li>   ');  }  p.push('  </ul> '); } return p.join('');

里面的 print 函數 在我們的模板里面是沒有用到的。

要指出的是,采用 push 的方法在 IE6-8 的瀏覽器下會比 += 的形式快,但是在現在的瀏覽器里面, += 是拼接字符串最快的方法。實測表明現代瀏覽器使用 += 會比數組 push 方法快,而在 v8 引擎中,使用 += 方式比數組拼接快 4.7 倍。所以 目前有些更高級的模板引擎會 根據 javascript 引擎特性采用了兩種不同的字符串拼接方式。

下面的代碼是摘自騰訊的 artTemplate 的, 根據瀏覽器的類型來選擇不同的拼接方式。功能越強大,所考慮的問題也會更多。

var isNewEngine = ''.trim;// '__proto__' in {}var replaces = isNewEngine? ["$out='';", "$out+=", ";", "$out"]: ["$out=[];", "$out.push(", ");", "$out.join('')"];

挑戰:有興趣的可以改用 += 來實現上面的代碼。

總結

模板引擎原理總結起來就是:先獲取html中對應的id下得innerHTML,利用開始標簽和關閉標簽進行字符串切分,其實是將模板劃分成兩部份內容,一部分是html部分,一部分是邏輯部分,通過區別一些特殊符號比如each、if等來將字符串拼接成函數式的字符串,將兩部分各自經過處理后,再次拼接到一起,最后將拼接好的字符串采用new Function()的方式轉化成所需要的函數。

目前模板引擎的種類繁多,功能也越來越強大,不同模板間實現原理大同小異,各有優缺,請按需選擇。

希望本文所述對大家JavaScript程序設計有所幫助。


注:相關教程知識閱讀請移步到JavaScript/Ajax教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品av在线播放| 美女视频久久黄| 亚洲欧美综合v| 久久成年人视频| 粉嫩老牛aⅴ一区二区三区| 欧美精品videos性欧美| 欧美成人高清视频| 成人在线视频福利| 日韩精品欧美国产精品忘忧草| 91精品国产91久久久久福利| 亚洲第一级黄色片| 国产成人精品电影| 国产午夜精品一区二区三区| 岛国视频午夜一区免费在线观看| 国产精品久久久91| 国产精品青草久久久久福利99| 亚洲成人久久电影| 国产精品久久久久7777婷婷| 色久欧美在线视频观看| 国产精品久久久久久久av电影| 亚洲人成网站免费播放| 亚洲午夜小视频| 国产精品久久久久久影视| 国产精品免费视频xxxx| 8050国产精品久久久久久| 亚洲激情成人网| 日韩亚洲成人av在线| 懂色av中文一区二区三区天美| 日韩在线观看免费高清完整版| 91精品国产免费久久久久久| 亚洲欧美国产制服动漫| 久久久亚洲影院你懂的| 欧美激情免费看| 欧美激情手机在线视频| 国产精品高潮呻吟久久av无限| 亚洲欧洲免费视频| 午夜精品久久久久久久久久久久久| 国产精品视频内| 色无极亚洲影院| 日韩精品在线观看视频| 久久99视频精品| 国产一区二区精品丝袜| 欧洲成人免费aa| 高清欧美性猛交| 久久视频在线观看免费| 亚洲女人天堂色在线7777| 最近2019中文字幕第三页视频| 国产午夜精品全部视频在线播放| 亚洲天堂av女优| 精品网站999www| 亚洲第一色中文字幕| 色悠悠久久88| 国产精品高潮呻吟久久av无限| 精品国产一区二区三区久久| 国产亚洲a∨片在线观看| 欧美性猛交xxxx乱大交3| 精品久久久久久久久久久久久久| 欧美性感美女h网站在线观看免费| 亚洲视频欧美视频| 日韩在线视频二区| 992tv在线成人免费观看| 国产亚洲激情在线| 91精品免费看| 日韩女优在线播放| 69av视频在线播放| 亚洲国产精彩中文乱码av在线播放| 午夜精品久久久99热福利| 国产精品999| 亚洲女人天堂色在线7777| 最近2019年日本中文免费字幕| 国产丝袜高跟一区| 夜夜躁日日躁狠狠久久88av| 日韩中文在线中文网三级| 福利视频一区二区| 亚洲成人精品久久| 亚洲男人第一网站| 日韩高清电影好看的电视剧电影| 国产视频欧美视频| 激情亚洲一区二区三区四区| 欧美电影免费观看大全| 国产视频自拍一区| 国产成人涩涩涩视频在线观看| 亚洲天堂一区二区三区| 亚洲字幕在线观看| 亚洲最大av网站| 97国产精品人人爽人人做| 亚洲欧美福利视频| 久久这里有精品| 日韩精品视频观看| 亚洲综合在线播放| 91黄色8090| 欧美大秀在线观看| 日韩中文在线中文网在线观看| 欧美一级黑人aaaaaaa做受| 日韩大片在线观看视频| 欧美一级大片在线观看| 亚洲a在线观看| 91免费综合在线| 日韩精品一区二区视频| 国产丝袜高跟一区| 亚洲精品国产免费| 91久久嫩草影院一区二区| 91国偷自产一区二区三区的观看方式| 国产乱肥老妇国产一区二| 亚洲欧美在线一区二区| 久久不射热爱视频精品| 国产精品99久久99久久久二8| xxav国产精品美女主播| 少妇高潮久久77777| 2023亚洲男人天堂| 国产精品免费久久久久久| 欧美日韩国产丝袜另类| 国产一区二区三区在线免费观看| 欧美高清激情视频| 欧美高清不卡在线| 国产欧美中文字幕| 欧美国产激情18| 精品视频在线播放色网色视频| 91中文在线观看| 日韩成人激情视频| 91久久久久久国产精品| 全球成人中文在线| 亚洲欧美日韩精品久久| 动漫精品一区二区| 日韩欧美亚洲一二三区| 97国产精品人人爽人人做| 欧美中文在线免费| 在线视频日本亚洲性| 黄色成人在线播放| 国产精品入口夜色视频大尺度| 日韩在线免费视频| 日韩三级影视基地| 亚洲日本欧美日韩高观看| 成人美女av在线直播| 欧美wwwxxxx| 17婷婷久久www| 国产aⅴ夜夜欢一区二区三区| 91精品国产自产在线观看永久| 亚洲**2019国产| 精品成人国产在线观看男人呻吟| 97超级碰碰碰久久久| 亚洲国产日韩一区| 色偷偷噜噜噜亚洲男人| 亚洲国产精品久久久久秋霞不卡| 国产精品第8页| 亚洲成**性毛茸茸| 一区二区三区国产在线观看| 日韩大片在线观看视频| 国产视频丨精品|在线观看| 欧美激情在线观看| 欧美丝袜美女中出在线| 国产精品高清免费在线观看| 91夜夜揉人人捏人人添红杏| 欧美老女人性生活| 亚洲激情久久久| 91精品视频在线免费观看| 欧美丝袜美女中出在线| 欧美极品第一页| 久久成人亚洲精品| 久久的精品视频| 欧美韩日一区二区| 日韩国产中文字幕| 成人激情在线播放| 亚洲第一二三四五区|