1. 程式人生 > >前端模板的原理與實現

前端模板的原理與實現

時下流行什麼react, avalon, angular, vue什麼,其核心都離不開前端模板。理解前端模板,是我們瞭解MV* 的關鍵。

前端框架最重要的目的是將頁面渲染出來。“渲染”(render)這個詞最初不是前端的東西的。前端之前叫做切圖,將設計師做的PSD變成一個靜態頁面,然後加上動態互動。但是我們有許多資料是來自後端,如何將資料加入靜態頁面呢?於是又多了一套工序叫“套頁面”。套頁面的過程實際就是將靜態頁面變成切割成一塊塊,每一塊都是一個php,jsp或vm檔案,它們是後端模板引擎的處理物件!

其實模板是不侷限於後端還是前端的, 模板的本質是用於從資料(變數)到實際的視覺表現(HTML程式碼)

這項工作的一種實現手段。由於後端近水樓臺先得月(取資料比較方便),因此先在後端發展出這種技術。這些後端模板檔案是活動於伺服器的,然後經過複雜的處理,最後由瀏覽器渲染出來。這時的渲染是將伺服器拼接好的靜態文字變成一個DOM樹的過程。

如果要實現前端實現MVC或MVP,那些工序必須發生改變。靜態檔案產出是不變,尤其是大公司,分工夠細,有專門的切圖組(大多數是妹子)將它們做出來。接著是套頁面,這時就不能使用後端模板引擎,需要引入前端模板引擎。由於實現一個前端模板引擎太簡單了,經過多年的發展,已經有眾多好用的輪子:

其它推薦的還有ejs, 易學易用,對有過ASP/PHP/JSP程式設計經驗的人來說,非常親切自然,缺點就是功能有點簡單。

其他doT, xtempalate, Underscore Templates。

最不推薦是jade, 有點華而不實,過度設計,導致套頁面工作量大,效能其差。

虛擬DOM時代流行的jsx就是無邏輯模板。之所以流行無邏輯或輕邏輯模板,其主要原因是改動成本比較少,像jade這樣自造語法糖太多,從美工手中拿來的html需要大動干戈,進行摧心折骨般的改造才能套資料。對於模板來說,最簡單而言,就是將某個可變資料放到適當的地方(填空),而其次,可以控制這個區域輸出不輸入(if指令),或讓其個區域迴圈輸入多次(for指令),更強制,實現模板互相套嵌(layout與block)。為了實現if與for有兩種方法,一種是單純的區域,插入一個js 語句,裡面有if語句與for語句,另一種是使用語法糖,比如說 ms-for, ms-repeat, ng-if, ng-repeat。語法糖的用法比直接使用JS語句簡單,但是帶來學習成本與拓展功能。每一個模板 if, for指令的語法都不一樣的,並且你想在迴圈做一些處理,比如過濾一些資料,或突然在某處中斷,這又得引用一些新的語句。隨著模板要求前後共用,就有了傳輸成本,直接寫JS語句在模板裡面肯定比不過語法糖。因此基於這種種原因,mustache風格的模板就成為主流。

現在三種模板風格:

PHP/ASP/JSP風格:

PHP
123456789 <%if(list.length){%><ol><%for(n=0;n<list.length;++n){%><li><%=list[n]%></li><%}%></ol><%}%>

mustcache風格,高階語法有限,通常難自定義拓展:

123456789 {{#if list.length}}<ol>{{#each list item}}<li>{{item}}</li>{{/each}}</ol>{{/if}}

屬性繫結風格:

12345 <ol ms-if="list.length"><li ms-for="item in list">{{item}}</li></ol>

前兩者只能出現於script, textarea等容器元素內部。因此<分隔符與標籤的<容器造成衝突,並且也不利於IDE的格式化處理。屬性繫結風格則是MVVM時期最流行的模板定義風格,某頁面某個區域就是一個模板,不需要進行入append等操作。

我們再來看如何實現前端模板。前端模板的本質就是一個可以轉換函式(渲染函式)的字串。渲染函式放進一個充滿資料的物件後,還原為一個全新的字串。因此重點是如何構建一個渲染函式。最簡單的方式是正則,還記得第二章的format方法嗎,這就是一個輕型的填充資料的方法。

JavaScript
12345678910111213 functionformat(str,object){vararray=Array.prototype.slice.call(arguments,1);returnstr.replace(/\\?\#{([^{}]+)\}/gm,function(match,name){if(match.charAt(0)=='\\')returnmatch.slice(1);varindex=Number(name)if(index>=0)returnarray[index];if(object&&object[name]!==void0)returnobject[name];return'';});}

format方法是通過#{ }來劃分靜態內容與動態內容的,一般來說它們稱之為定界符(delimiter)。#{為前定界符,}為後界符,這個#{}其實是ruby風格的定界符。通常的定界符是<%%>{{}} 。通常在前定界符中還有一些修飾符號,比如=號,表示這個會輸出到頁面,-號,表示會去掉兩旁的空白。
將下例,要編譯成一個渲染函式

JavaScript
12345 vartpl='你好,我的名字啊<%name%>, 今年已經 <%info.age%>歲了'vardata={name:"司徒正美",age:20}

大抵是這樣

JavaScript
12 varbody='你好,我的名字叫'+data.name+', 今年已經 '+data.info.age+'歲了'varrender=newFunction('data','return '+body)

或者聰明一點,使用陣列來join:

JavaScript
1234567 vararray=['return ']array.push('你好,我的名字叫'array.push(data.name)array.push(', 今年已經')array.push(data.info.age)array.push('歲了')varrender=newFunction('data',array.join('+'))

這就得區分靜態內容與為變數前加data.字首。這一步可以用正則來做,也可以用純字串。我們試一下純字串方式。假令前定界符為openTag,後定界符為closeTag,通過indexOf 與slice方法,就可以將它切成一塊塊。

JavaScript
123456789101112131415161718192021222324252627282930 functiontokenize(str){varopenTag='<%'    var closeTag = '%>'varret=[]do{varindex=str.indexOf(openTag)index=index===-1?str.length:indexvarvalue=str.slice(0,index)//抽取{{前面的靜態內容ret.push({expr:value,type:'text'})//改變str字串自身str=str.slice(index+openTag.length)if(str){index=str.indexOf(closeTag)varvalue=str.slice(0,index)//抽取{{與}}的動態內容ret.push({expr:value.trim(),//JS邏輯兩旁的空白可以省去type:'js'})//改變str字串自身str=str.slice(index+closeTag.length)}}while(str.length)returnret}console.log(tokenize(tpl))

2090589628-57e4f8175d723_articlex

然後通過render方法將它們拼接起來。

JavaScript
123456789101112 functionrender(str){vartokens=tokenize(str)varret=[]for(vari=0,token;token=tokens[i++];){if(token.type==='text'){ret.push('"'+token.expr+'"')}else{ret.push(token.expr)}}console.log("return "+ret.join('+'))}

打印出來如下

JavaScript
1 return"你好,我的名字叫"+name+", 今年已經 "+info.age+"歲了"

這個方法還不完整。首先光是在兩旁加上雙引號是不可靠的,萬一裡面還有雙引號怎麼辦。因此我們需要引入第二章介紹的quote方法,當型別為文字時,ret.push(+quote(token.expr)+)。其次需要對動態部分的變數加上.data。怎麼知道它是一個變數呢。我們回想一下變數的定義,就是以_,$或字母開頭的字元組合。為了簡潔起見,我們暫時不用裡會中文的情況。不過,info.age這個字串裡面,其實有兩個符合變數的子串,而我只需要在info前面加data.。這時,我們需要設法在匹配變數前,將物件的子級屬性替換掉,替換成不符合變數的字元,然後再替換為去。為此,我搞了一個dig與fill方法,將子級屬性變成??12這樣的字串:

JavaScript
12345678910111213141516171819202122232425262728293031323334 varquote=JSON.stringify//自己到第二章找完整函式varrident=/[$a-zA-Z_][$a-zA-Z0-9_]*/gvarrproperty=/\.\s*[\w\.\$]+/gvarnumber=1varrfill=/\?\?\d+/gvarstringPool={}functiondig(a){varkey='??'+number++stringPool[key]=areturnkey}function

相關推薦

前端模板原理實現

時下流行什麼react, avalon, angular, vue什麼,其核心都離不開前端模板。理解前端模板,是我們瞭解MV* 的關鍵。 前端框架最重要的目的是將頁面渲染出來。“渲染”(render)這個詞最初不是前端的東西的。前端之前叫做切圖,將設計師做的PSD變成一個靜態頁

C++標準模板庫(STL)迭代器的原理實現

引言 迭代器(iterator)是一種抽象的設計理念,通過迭代器可以在不瞭解容器內部原理的情況下遍歷容器。除此之外,STL中迭代器一個最重要的作用就是作為容器(vector,list等)與STL演算法的粘結劑,只要容器提供迭代器的介面,同一套演算法程式碼可以利

Java 線程池的原理實現

控制 try 所在 使用 urn str waiting media .info 這幾天主要是狂看源程序,在彌補了一些曾經知識空白的同一時候,也學會了不少新的知識(比方 NIO)。或者稱為新技術吧。 線程池就是當中之中的一個,一提到線程。我們會想到曾經《操作系統》的

防盜鏈的基本原理實現

rec eal limit ole 站點 new exceptio stub text 1. 我的實現防盜鏈的做法,也是參考該位前輩的文章。基本原理就是就是一句話:通過判斷request請求頭的refer是否來源於本站。(當然請求頭是來自於客戶端的,是可偽造的,暫不在本文

最小二乘法多項式曲線擬合原理實現 zz

博客 del p s 並且 多項式 聯網 python mar 程序 概念 最小二乘法多項式曲線擬合,根據給定的m個點,並不要求這條曲線精確地經過這些點,而是曲線y=f(x)的近似曲線y= φ(x)。 原理 [原理部分由個人根據互聯網上的資料進行總結,希望對大

無限極分類原理實現(轉)

轉換 完成 外灘 獲得 意思 容易 set 導航 另一個   前言   無限極分類是我很久前學到知識,今天在做一個項目時,發現對其概念有點模糊,所以今天就來說說無限極分類。   首先來說說什麽是無限極分類。按照我的理解,就是對數據完成多次分類,如同一棵樹一樣,從根開始,

java監聽器的原理實現

來看 class copyto 圖片 http size stat 順序 方法 監聽器模型涉及以下三個對象,模型圖如下: (1)事件:用戶對組件的一個操作,稱之為一個事件 (2)事件源:發生事件的組件就是事件源 (3)事件監聽器(處理器):監聽並負責處理事件的方法 執行順序

Redis實現分布式鎖原理實現分析

數據表 防止 中一 csdn 訂單 not 產生 www 整體 一、關於分布式鎖 關於分布式鎖,可能絕大部分人都會或多或少涉及到。 我舉二個例子: 場景一:從前端界面發起一筆支付請求,如果前端沒有做防重處理,那麽可能在某一個時刻會有二筆一樣的單子同時到達系統後臺。 場

優先隊列原理實現

() 通過 size 大堆 默認 深入理解 -s 示例 完整 轉自:https://www.cnblogs.com/luoxn28/p/5616101.html 優先隊列是一種用來維護一組元素構成的結合S的數據結構,其中每個元素都有一個關鍵字key,元素之間的比較都是通過k

LVM原理實現過程

LVM原理與實現過程一、什麽是LVM 不管是使用傳統的MBR分區方式或者是GPT的分區方式,在最後數據量逐漸變大的過程中都會出現空間不足的情況,但是若是使用將此分區的數據全部遷移至一個更大空間的磁盤上的遷移時間也是不可想象的,為了解決這個問題,LVM就誕生了。LVM(Logical volume Manag

MapReduce原理實現

讀取 提交 hdf 撲克 datanode 分配 去掉 是否 跟著 課程鏈接:Hadoop大數據平臺架構與實踐--基礎篇 1.MapReduce原理 分而治之,一個大任務分成多個小的子任務(map),並行執行後,合並結果(reduce) 問題1:1000副撲克牌少哪一張牌(

單點登錄原理實現

授權 速度 restful contain ppi 靠譜 遠的 except 令牌 單點登錄原理與實現      關於單點登錄,在項目中用到的是對於cookie中設置的domain 為二級域名,這樣二級域名下的cookie都可以共享,將sessionId存儲在cookie中

數據加密--詳解 RSA加密算法 原理實現

pri mir 對稱加密 模運算 速度 探討 進制 成績 分析 RSA算法簡介 RSA是最流行的非對稱加密算法之一。也被稱為公鑰加密。它是由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)在19

線上防雪崩利器——熔斷器設計原理實現

data 沒有 保障系統 狀態模式 熔斷器 data- 雪崩 form cimage 前言 這是一篇根據工作中遇到的問題總結出的最佳實踐。 上周六,我負責的業務在淩晨00-04點的支付全部失敗了。 結果一查,MD,晚上銀行維護,下遊支付系統沒有掛維護公告,在此期間一直請求維

分頁技術原理實現之分頁的意義及方法(一)

轉載自https://www.jb51.net/article/86326.htm。 什麼是分頁技術  分頁,是一種將所有資料分段展示給使用者的技術.使用者每次看到的不是全部資料,而是其中的一部分,如果在其中沒有找到自習自己想要的內容,使用者可以通過制定頁碼或是翻頁的方式轉換可見內容,

Android系統硬體抽象層原理實現之WIFI

http://m.blog.csdn.net/linux_zkf/article/details/7492720 整個WIFIHAL實現都很簡單,都是對wpa_supplicant的操作和使用,如果需要自己實現 WIFI HAL可以參考wifi.c來實現wifi.h中所定義的

推薦系統-協同過濾原理實現

一、基本介紹 1. 推薦系統任務 推薦系統的任務就是聯絡使用者和資訊一方面幫助使用者發現對自己有價值的資訊,而另一方面讓資訊能夠展現在對它感興趣的使用者面前從而實現資訊消費者和資訊生產者的雙贏。 2. 與搜尋引擎比較 相同點:幫助使用者快速發現有用資訊的工具 不同點:和搜尋引擎不同的是推薦系統不

離散傅立葉變換(DFT)和快速傅立葉變換(FFT)原理實現

目錄 1、影象變換 2、離散傅立葉變換(Discrete Fourier Transform) 3、DFT性質 4、DFT與數字影象處理 5、FFT-快速傅立葉變換 6、DFT與FFT的演算法實現 1. 影象變換 — —數學領域中有很多種變換,如傅立葉變換、拉普拉斯變

DeepLearning(深度學習)原理實現

經過三年的狂刷理論,覺得是時候停下來做些有用的東西了,因此決定開博把他們寫下來,一是為了整理學過的理論,二是監督自己並和大家分享。先從DeepLearning談起吧,因為這個有一定的實用性(大家口頭傳的“和錢靠的很近”大笑),國內各個大牛也都談了不少,我儘量從其他方面解釋一下。

什麼是單點登入(原理實現簡介)

單系統登入機制 1、http無狀態協議   web應用採用browser/server架構,http作為通訊協議。http是無狀態協議,瀏覽器的每一次請求,伺服器會獨立處理,不與之前或之後的請求產生關聯,這個過程用下圖說明,三次請求/響應對之間沒有任何聯絡。   但這也同時意味著,任何使用者都能通過