1 頁面級的渲染
再剛有web的時候,前端與后端的交互,非常直白,瀏覽器端發出URL,后端返回一張拼好了的HTML串。瀏覽器對其進行渲染。html中可能會混有一些php(或者php中混有一些html)。在服務端將數據與模板進行拼裝,生成要返回瀏覽器端的html串。
這與我們現在做一個普通網頁沒什么區別。只不過現在,我們更常使用模板技術來解決前后端耦合的問題。
前端使用模板引擎,在html中寫一些標簽,與數據與邏輯基本無關。后端在渲染的時候,解析這些標簽,生成HTML串,如smarty。其實前端與后端的交互在服務端就已經有一次了。
模板:
front.tpl <div> {%$a%} </div>
后端:
// 設置變量 $smarty->assign('a', 'give data'); // 展示模板 $smarty->display("front.tpl");
到前端時是渲染好的html串:
<div> give data </div>
這種方式的特點是展示數據快,直接后端拼裝好數據與模板,展現到用戶面前。
2 異步的請求與新增模板
新的時代,由ajax引領。(Asynchronous Javascript And XML),這種技術的歷史,我就不再贅述。ajax的用法也有多種。
ajax接受各種類型的返回。包括XML/JSON/String等。前端發起ajax請求,后端直接將數據返回。
但是,讀者們有沒有想過,ajax回來的數據是干嘛用的呢?相信大部分人使用ajax拿回的數據是用來展示的。前端得把ajax拿回來的數據與模板進行拼裝。這就面臨了一個問題,當你的模板非常“華麗”的時候(也就是模板代碼比較多的時候)。我們在前端寫的拼字符串的邏輯,會非常的復雜。
也有的人圖省事,直接就在ajax的返回值中,傳輸拼裝好的html字符串。這樣可以直接把ajax拿到的html字符串,填充到頁面上。
下面實例說明一下兩種方式:
2.1 ajax獲取字符串直接渲染方式
如圖2.1.1所示:
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>下面是待填充區域:</h1> <div class="blankPlace"></div> <script> var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) { document.querySelector('.blankPlace').innerHTML = xhr.responseText; } }; xhr.open('GET', './a.html'); xhr.send(null); </script> </body> </html> ======================================================================== a.html <h2>我是模板</h2> <div>這是請求回來的數據</div>

圖2.1.1
2.2 ajax獲取數據,前端進行拼裝的方式
效果如圖2.2.1所示:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>下面是待填充區域:</h1> <div class="blankPlace"></div> <script> var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status >= 200 && xhr.status < 300) { var res = JSON.parse(xhr.responseText); document.querySelector('.blankPlace').innerHTML = '' +'<h2>' + '我是模板' +'</h2>' +'<div>' + res.data +'</div>'; } }; xhr.open('GET', './b.json'); xhr.send(null); </script> </body> </html> ================================================ b.json {"data": "這是請求回來的數據"}

圖 2.2.1
2.3 兩種方式的權衡
那么,如何權衡兩種方式呢?
筆者單從自己的思維考慮,得出以下結論。如果這種模板的拼裝會發生多次。是一個非常頻繁的行為,且模板基本一致,只是數據變動的話,最好是一開始采用客戶端拼裝的方法。因為,同樣的模板沒有必要被傳到客戶端好幾次。這樣,我們可以剩下傳輸同樣模板的流量,請求更快。
類似於新聞流這種網站比較適合這種方式,如今日頭條,如圖2.3.1所示:

圖2.3.1

圖2.3.2
筆者在DOM上面打了斷點后,找到了其拼裝模板,確是在客戶端所做。
不過,這種做法也有問題,就是用戶同步刷新的時候,需要等頁面渲染完,再發一個請求,去請求第一屏的數據,才能開始渲染。這個過程相當於發了兩次請求,等待的時候還是有所感知的,如圖2.3.3所示。

圖2.3.3
所以這種方式也是有些不盡人意的地方的。經過查看,網易新聞的web版,今日頭條的web版,天天快報的web版均是采用這種方式。
第二種方式,同步的時候,就將一段渲染好的HTML,直接輸出到頁面,而在異步的時候,請求的也是這段HTML,直接將請求回的HTML往頁面上一塞就完成了。這樣就可以達到同步頁面的時候,直接輸出,用戶就不會看到等待中的小菊花了。
百度首頁就采取了這種方式。新聞直出,無需等待如圖2.3.4:

圖2.3.4
但是每次請求新聞的時候,也會去請求HTML片段,如圖2.3.5所示:

圖2.3.5
這種方式雖然首屏較快,但是,每次傳輸同樣的新聞模板也是需要浪費不少模板流量的。
2.4 混合方式
看過了上述兩種方式,聰明的你肯定會想:如果前端的js里寫一份模板,后端的html(jsp/asp/smarty)中也寫一份模板呢?這樣,同步的時候,直接用后端HTML(jsp/asp/smarty)中的模板。異步拉取數據的時候,每次使用js中的模板進行拼裝 。同步也能保證首屏的速度,異步也能保證傳輸量的限制與速度。可是這樣,也會面臨問題,那就是,你的模板需要維護兩份。如果那天產品和你說,我要改一下頁面的結構。你不得不改動HTML的時候。js中與jsp/asp/smarty中的模板都需要同樣的更改兩次。
2.5 前端的模板引擎
如果說,后端可以將html的拼裝轉變為使用引擎的話,前端為什么不可以呢?這里我先給大家寫一個非常簡單的模板解析函數,效果如圖2.5.1:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <div id="content1"></div> <div id="content2"></div> <script> // 這是我們的模板,怎么樣,比直接寫html字符串拼裝看起來清爽多了吧? var template = '' +'<div>' + '{%=a%}' + '{%if (a===1){%}' + '<span>' + 'a是1' + '</span>' + '{%}%}' +'</div>'; // 能解析輸出與if條件語句的函數 function TEMPLATEparser(template, variables) { // 語法替換 var funcStr = template .replace(/\{\%\=(\w+)\%\}/, function (code, variable) { return '"; str += "' + variable + '"; str += "'; }) .replace(/\{\%(if.*?)\%\}(.*?)\{\%(\})\%\}/, function (code, judge, content, end) { return '";' + judge + 'str+="' + content + '";' + end + 'str += "'; }); // 返回拼裝函數 return new Function(variables, 