淺談 Web 中前后端模板引擎的使用(轉(zhuǎn)載)

原地址:https://github.com/hanzichi/underscore-analysis/issues/25

前言
這篇文章本來(lái)不打算寫的,實(shí)話說(shuō)樓主對(duì)前端模板的認(rèn)識(shí)還處在非常初級(jí)的階段,但是為了整個(gè) 源碼解讀系列 的完整性,在深入 Underscore _.template 方法源碼后,覺(jué)得還是有必要記下此文,為了自己備忘也好,為了還沒(méi)用上前端模板引擎的同學(xué)的入門也好。(熟悉模板引擎的可以幫樓主看看文中有沒(méi)有 BUG ..)
后端 MVC
說(shuō)起模板渲染,樓主首先接觸的其實(shí)并不是前端模板引擎,而是后端。后端 MVC 模式中,一般從 Model 層中讀取數(shù)據(jù),然后將數(shù)據(jù)傳到 View 層渲染(渲染成 HTML 文件),而 View 層,一般都會(huì)用到模板引擎,比如樓主項(xiàng)目中用到的 PHP 的 smarty 模板引擎。隨便上段代碼感受一下。

<div>
  <ul class="well nav nav-list" style="height:95%;">
    {{foreach from=$pageArray.result item=leftMenu key=key name=leftMenu}}
      <li class="nav-header">{{$key}}</li>
      {{foreach from=$leftMenu key=key2 item=item2}}
        <li><a target="main" href='{{$item2}}'>{{$key2}}</a></li>
      {{/foreach}}
    {{/foreach}}
  </ul>
</div>

傳入 View 層的其實(shí)就是個(gè)叫做 $pageArray 的 JSON 數(shù)據(jù)。而 MVC 模式也是非常容易理解,推薦看下阮一峰老師的 談?wù)凪VC模式,然后再看看下面這張圖。


以前的 WEB 項(xiàng)目大多會(huì)采用這種后臺(tái) MVC 模式,這樣做有利于 SEO,并且與前端請(qǐng)求接口的方式相比,少了個(gè) HTTP 請(qǐng)求,理論上加載速度可能會(huì)稍微快些。但是缺點(diǎn)也非常明顯,前端寫完靜態(tài)頁(yè)面,要讓后臺(tái)去「套模板」,每次前端稍有改動(dòng),后臺(tái)對(duì)應(yīng)的模板頁(yè)面同時(shí)也需要改動(dòng),非常麻煩。頁(yè)面中如果有復(fù)雜的 JS,前端寫還是后端寫?前端寫的話,沒(méi)有大量的數(shù)據(jù),調(diào)試不方便,后端寫的話... 所以樓主看到的 PHPer 通常都會(huì) JS。
前端模板
AJAX 的出現(xiàn)使得前后端分離成為可能。后端專注于業(yè)務(wù)邏輯,給前端提供接口,而前端通過(guò) AJAX 的方式向后端請(qǐng)求數(shù)據(jù),然后動(dòng)態(tài)渲染頁(yè)面。
我們假設(shè)接口數(shù)據(jù)如下:

[{name: "apple"}, {name: "orange"}, {name: "peach"}]

假設(shè)渲染后的頁(yè)面如下:

<div>
  <ul class="list">
    <li>apple</li>
    <li>orange</li>
    <li class="last-item">peach</li>
  </ul>
</div>

前端模板引擎出現(xiàn)之前,我們一般會(huì)這么做:

<div></div>
<script>
// 假設(shè)接口數(shù)據(jù)
var data = [{name: "apple"}, {name: "orange"}, {name: "peach"}];

var str = "";
str += '<ul class="list">';

for (var i = 0, len = data.length; i < len; i++) {
  if (i !== len - 1)
    str += "<li>" + data[i].name + "</li>";
  else
    str += '<li class="last-item">'  + data[i].name + "</li>";
}

str += "</ul>";
document.querySelector("div").innerHTML = str;
</script>

其實(shí)樓主個(gè)人也經(jīng)常這么干,看上去簡(jiǎn)單方便,但是這樣做顯然有缺點(diǎn),將 HTML 代碼(View 層)和 JS 代碼(Controller 層)混雜在了一起,UI 與邏輯代碼混雜在一起,閱讀起來(lái)會(huì)非常吃力。一旦業(yè)務(wù)復(fù)雜起來(lái),或者多人維護(hù)的情況下,幾乎會(huì)失控。而且如果需要拼接的 HTML 代碼里有很多引號(hào)的話(比如有大量的 href 屬性,src 屬性),那么就非常容易出錯(cuò)了(這樣干過(guò)的應(yīng)該深有體會(huì))。
這個(gè)時(shí)候,前端模板引擎出現(xiàn)了,Underscore 的 _.template 可能是最簡(jiǎn)單的前端模板引擎了(可能還上升不到引擎的高度,或者說(shuō)就是個(gè)前端模板函數(shù))。我們先不談 _.template 的實(shí)現(xiàn),將以上的代碼用其改寫。

<div></div>
<script src="http://cdn.bootcss.com/underscore.js/1.8.3/underscore.js"></script>
<script type="text/template" id="tpl">
  <ul class="list">
    <%_.each(obj, function(e, i, a){%>
      <% if (i === a.length - 1) %>
        <li class="last-item"><%=e.name%></li>
      <% else %>
        <li><%=e.name%></li>
    <%})%>
  </ul>
</script>

<script>
// 模擬數(shù)據(jù)
var data = [{name: "apple"}, {name: "orange"}, {name: "peach"}];

var compiled = _.template(document.getElementById("tpl").innerHTML);
var str = compiled(data);
document.querySelector("div").innerHTML = str;
</script>

這樣一來(lái),如果前端需要改 HTML 代碼,只需要改模板即可。這樣做的優(yōu)點(diǎn)很明顯,前端 UI 和邏輯代碼不再混雜,閱讀體驗(yàn)良好,改動(dòng)起來(lái)也方便了許多。
前后端分離最大的缺點(diǎn)可能就是 SEO 無(wú)力了,畢竟爬蟲(chóng)只會(huì)抓取 HTML 代碼,不會(huì)去渲染 JS。(PS:現(xiàn)在的 Google 爬蟲(chóng)已經(jīng)可以抓取 AJAX 了 Making AJAX applications crawlable,具體效果未知)
Node 中間層
單純的后端模板引擎(后端 MVC)以及前端模板引擎方式都有一定的局限性,Node 的出現(xiàn)讓我們有了第三種選擇,讓 Node 作為中間層。
具體如何操作?簡(jiǎn)單地說(shuō)就是讓一門后臺(tái)語(yǔ)言(比如 Java?PHP?)單純提供渲染頁(yè)面所需要的接口,Node 中間層用模板引擎來(lái)渲染頁(yè)面,使得頁(yè)面直出。這樣一來(lái),后臺(tái)提供的接口,不僅 Web 端可以使用,APP,瀏覽器也可以調(diào)用,同時(shí)頁(yè)面 Node 直出也不會(huì)影響 SEO,并且前后端也分離,不失為一種比較完美的方案。
總結(jié)
本文簡(jiǎn)單介紹了模板引擎在前后端的使用,下文我們回到 Underscore,重點(diǎn)分析下 _.template 的使用方式以及源碼原理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,530評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,407評(píng)論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,981評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,759評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,204評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,415評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,955評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,650評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,892評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,675評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,967評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,658評(píng)論 25 708
  • 要看就看自然的風(fēng)景 要聽(tīng)就聽(tīng)天籟的聲音 要想就想溫暖的往事 要夢(mèng)就夢(mèng)快樂(lè)的純真 不錯(cuò)過(guò)每一個(gè)清爽的早晨 不放棄每一...
    飛鏑閱讀 243評(píng)論 0 0
  • 簡(jiǎn)悅直播教練恬源閱讀 183評(píng)論 0 2
  • 第一篇在簡(jiǎn)書(shū)上的文章,以前都是寫在博客上,但是也得跟上時(shí)代的步伐不是..雖然有點(diǎn)晚 -.- 1.在searchB...
    IT_機(jī)器貓閱讀 318評(píng)論 0 2
  • 說(shuō)起2017年,也僅剩下不到兩個(gè)月的時(shí)間。過(guò)去的十個(gè)月里,我過(guò)的倉(cāng)促而又忙碌,有歡笑,有哭泣,有得意,有失意,有感...
    王道奇人閱讀 334評(píng)論 0 0