关于模板引擎一
?
前端模板引擎需要有開發(fā)時(shí)的透明性透明性即指我在搭建好開發(fā)環(huán)境后,隨手寫代碼隨手刷新瀏覽器就能看到最新的效果,而不需要額外地執(zhí)行任何命令或有任何的等待過程。
所以一切依賴編譯過程的模板引擎并不適合前端使用,編譯只能是模板引擎的一個(gè)特性,而不能是使用的前提
更嚴(yán)格地說,使用FileWatch等手段進(jìn)行文件變更檢測(cè)并自動(dòng)編譯也不在我的考慮范圍之內(nèi),因?yàn)檫@會(huì)造成額外的等待,
由此可以推出,前端的模板引擎應(yīng)該是具備可在純前端環(huán)境中解析使用的能力的。
前端模板引擎要有良好的運(yùn)行時(shí)調(diào)試能力由于用戶行為的不確定性、執(zhí)行環(huán)境的不確定性、各種第三方腳本的影響等,前端很難做到完全的錯(cuò)誤處理和跟蹤,這也導(dǎo)致前端必然存在需要直接在線上排查問題的情況
而當(dāng)問題出現(xiàn)在模板引擎這一層時(shí),就需要模板引擎提供良好的調(diào)試能力
一般來(lái)說,編譯后生成的函數(shù)的調(diào)試能力是弱于原先手動(dòng)編寫的模板片斷的,因?yàn)樽詣?dòng)生成的函數(shù)基本不具備可讀性和可斷點(diǎn)跟蹤性
因此在這一點(diǎn)上,一個(gè)供前端使用的模板引擎應(yīng)該具備在特定情況下從“執(zhí)行編譯后函數(shù)獲取HTML”換回“解析原模板再執(zhí)行函數(shù)獲取HTML”的模式,即應(yīng)該支持在兩種模式間切換
或者更好地,一個(gè)強(qiáng)大的前端模板引擎編譯生成的函數(shù),可以使用Source Map或其它自定義的手段直接映射回原模板片段,不過現(xiàn)在并沒有什么模板引擎實(shí)現(xiàn)了這一功能
前端模板引擎要對(duì)文件合并友好 在HTTP/2普及之前,文件合并依舊是前端性能優(yōu)化中的一個(gè)重要手段,模板作為文件的一部分,依舊是需要合并的在提供編譯功能的模板引擎中,我們可以使用編譯的手段將模板變?yōu)镴avaScript源碼,再在JavaScript的基礎(chǔ)上做文件合并
但是如果我們出于上文所說的調(diào)試能力等原因希望保留原模板片段,那就需要模板引擎本身支持模板片段合并為一個(gè)文件了
大部分僅支持將一段輸入的字符串作為模板解析的引擎并不具備這一能力,他們天生并不能將一整個(gè)字符串切分為多個(gè)模板片段,因而無(wú)法支持模板片段層面上的文件合并
需要實(shí)現(xiàn)對(duì)文件合并的支持,最好的辦法就是讓模板的語(yǔ)法是基于“片段”的
前端模板引擎要擔(dān)負(fù)XSS的防范從安全性上來(lái)說,前端對(duì)XSS的控制是有嚴(yán)格要求的
前端對(duì)XSS的防范比較合適的方法是使用“默認(rèn)轉(zhuǎn)義”的白名單式策略
基于此,一個(gè)合理的模板引擎是必須支持默認(rèn)轉(zhuǎn)義的,即所有數(shù)據(jù)的輸出都默認(rèn)經(jīng)過escape的邏輯處理,將關(guān)鍵符號(hào)轉(zhuǎn)為對(duì)應(yīng)的HTML實(shí)體符號(hào),以從根源上杜絕XSS的入侵路徑
當(dāng)然并不是所有的內(nèi)容都必須經(jīng)過轉(zhuǎn)義的,在系統(tǒng)中免不了有對(duì)用戶輸入富文本的需求,因此需要支持特定的語(yǔ)法來(lái)產(chǎn)生無(wú)轉(zhuǎn)義的輸出,但時(shí)刻注意無(wú)轉(zhuǎn)義輸出才是特例,默認(rèn)情況下必須是轉(zhuǎn)義輸出的
前端模板引擎要支持片段的復(fù)用 這并不是前端模板引擎的需求,事實(shí)上任何模板引擎都應(yīng)該支持片段的復(fù)用,后端如Velocity、Smarty等無(wú)不擁有此功能所謂片段復(fù)用,應(yīng)該有以下幾個(gè)層次的應(yīng)用:
滿足第1和第2點(diǎn)的模板引擎并不少,而滿足第3點(diǎn)的前端模板引擎卻不多見,而后端的Razor、Smarty等都具備這一功能
前端模板引擎要支持?jǐn)?shù)據(jù)輸出時(shí)的處理所謂數(shù)據(jù)輸出時(shí)處理,指一個(gè)數(shù)據(jù)要在輸出時(shí)做額外的轉(zhuǎn)換,最常見的如字符串的trim操作,比較技術(shù)性的如markdown的轉(zhuǎn)換等
誠(chéng)然數(shù)據(jù)的轉(zhuǎn)換完全可以在將數(shù)據(jù)交給模板引擎前就通過JavaScript的邏輯處理完,但這會(huì)導(dǎo)致不少有些丑陋又有些冗余的代碼,對(duì)邏輯本身的復(fù)用性也會(huì)造成負(fù)面的影響
通常模板引擎對(duì)數(shù)據(jù)做額外處理會(huì)使用filter的形式實(shí)現(xiàn),類似bash中的管道的邏輯。filter的實(shí)現(xiàn)和注冊(cè)也會(huì)有不同的設(shè)計(jì),如mustache其實(shí)注冊(cè)的是fitler工廠,而另一些模板引擎則會(huì)直接注冊(cè)filter本身,不同設(shè)計(jì)有不同的考量點(diǎn),我們很難說誰(shuí)好誰(shuí)壞?
但是,模板引擎支持?jǐn)?shù)據(jù)的輸出處理后,會(huì)另我們?cè)诰幋a過程中產(chǎn)生一個(gè)新的糾結(jié),即哪些數(shù)據(jù)處理應(yīng)該交由模板引擎的filter實(shí)現(xiàn),哪些應(yīng)該在交給模板引擎前由自己的邏輯邏輯實(shí)現(xiàn)。這個(gè)話題展開來(lái)又是一篇長(zhǎng)長(zhǎng)的論述,于當(dāng)前的話題無(wú)關(guān)就略過吧
前端模板引擎要支持動(dòng)態(tài)數(shù)據(jù)在開發(fā)過程中,其實(shí)有不少數(shù)據(jù)并不是靜態(tài)的,如EmberJS就提供了Computed Property這樣的概念,Angular也有類似的東西,Backbone則可以通過重寫Model的get方法來(lái)變相實(shí)現(xiàn)
雖然ES5在語(yǔ)言層面上直接提供了getter的支持,但我們?cè)谇岸碎_發(fā)的大部分場(chǎng)景下依舊不會(huì)使用這一語(yǔ)言特性,而會(huì)選擇將動(dòng)態(tài)的數(shù)據(jù)封裝為某種對(duì)象的get等方法
而模板引擎在將數(shù)據(jù)轉(zhuǎn)為HTML片段的過程中,同樣應(yīng)該關(guān)注這一點(diǎn),對(duì)這些動(dòng)態(tài)計(jì)算的數(shù)據(jù)有良好的支持
說得更明白一些,模板引擎不應(yīng)該僅僅接受純對(duì)象(Plain Object)作為輸入,而應(yīng)該更開放地接受類似帶有g(shù)et方法的動(dòng)態(tài)的數(shù)據(jù)
一個(gè)比較合理的邏輯是,如果一個(gè)對(duì)象有一個(gè)get方法(模板引擎決定這個(gè)接口),則數(shù)據(jù)通過該方法獲取,其它情況下視輸入的對(duì)象為純對(duì)象(Plain Object),使用標(biāo)準(zhǔn)的屬性獲取邏輯
前端模板引擎要與異步流程嚴(yán)密結(jié)合一個(gè)很常見的例子是,我們有一個(gè)AMD模塊存放了全局使用的常量,模板引擎需要使用這些常量。當(dāng)然我們可以在使用模板引擎之前讓JavaScript去異步獲取這一模塊,隨后將常量作為數(shù)據(jù)傳遞給模板引擎,但這是一種業(yè)務(wù)與視圖相對(duì)耦合的玩法,出于強(qiáng)迫癥我并不覺得這是一個(gè)漂亮的設(shè)計(jì),所以我們希望
- 模板的輸出本身成了異步的方法,而不再像現(xiàn)在一樣直接返回字符串
- 分析模板對(duì)異步操作的依賴,整個(gè)字符串的拼接邏輯被打斷成多個(gè)異步
- 異步是需要等待的,且等待是未知的,從性能上考慮,是否需要考慮Stream式的輸出,以便完成一段提供一段
- 是提供內(nèi)置的固定幾種異步邏輯,還是基于Promise支持任何自定義的異步邏輯,在復(fù)雜度和實(shí)用性上作出平衡
至今我還沒有完全明確模板與異步結(jié)合的方式和接口,這個(gè)話題也沒辦法繼續(xù)深入探討了
前端模板引擎要支持不同的開發(fā)模式 前端發(fā)展至今,有很多不同的開發(fā)模式,比如:- 最普通的HTML頁(yè)面,使用DOMContentLoaded等事件添加邏輯,特定交互下局部刷新頁(yè)面
- 采用傳統(tǒng)的MVC模型進(jìn)行單頁(yè)式開發(fā)
- 使用MVVM方式以數(shù)據(jù)為核心,數(shù)據(jù)與視圖方向綁定進(jìn)行開發(fā)
- 基于Immutable Data進(jìn)行數(shù)據(jù)比對(duì)Diff轉(zhuǎn)DOM更新的開發(fā)(其中可能有Virtual DOM的引入)
- 能夠從模板中提取“這一模板對(duì)哪些數(shù)據(jù)有依賴”的元信息
- 能夠知道一個(gè)數(shù)據(jù)變化引擎的是模板的哪一塊,而不至于整個(gè)刷新
而通用模板引擎很少提供這兩個(gè)特性,所以沒辦法對(duì)不同的前端開發(fā)模式進(jìn)行全面到位的支持
從模板引擎本身的實(shí)現(xiàn)上來(lái)說,一種方法是直接將模板解析后的類似AST的結(jié)構(gòu)暴露出去,供其他框架合理地處理,同時(shí)提供對(duì)模板局部的刷新功能(也可與前面所說的模板片段一起考慮),但是大部分模板引擎為了性能等考慮,是不會(huì)解析出類似AST的語(yǔ)法結(jié)構(gòu)來(lái)的
在大型的前端項(xiàng)目,特別是單頁(yè)式的項(xiàng)目中,會(huì)有完全未知個(gè)數(shù)的模板片段同時(shí)存在,如果這些片段是帶有名稱(出于復(fù)用的考慮)的,就很容易造成名稱上的沖突
對(duì)于同一層級(jí)的邏輯(如大家都是業(yè)務(wù)層代碼,或者大家都是控件層代碼),名稱沖突是可以通過一些開發(fā)時(shí)的約定來(lái)解決的。但不同層之間,由于封裝性的要求,外部不應(yīng)該知道一些僅內(nèi)部使用的片段的名稱,此時(shí)如果不幸有名稱與其它層有沖突,會(huì)讓情況變得比較麻煩,這類問題甚至都不容易跟蹤,往往會(huì)導(dǎo)致大量的精力和時(shí)間的浪費(fèi)
因此,一個(gè)好的模板引擎應(yīng)該是多實(shí)例的,且不同實(shí)例間應(yīng)該相互具備隔離性,不會(huì)出現(xiàn)這種不可預(yù)期的沖突
將這個(gè)話題再往深地研究,就會(huì)發(fā)現(xiàn)單純的隔離是不夠的,不同層間除了不沖突的需求,同樣還有片段復(fù)用的需求,我們還會(huì)需要不同模板實(shí)例間可以開放一些固定的片段共享,因此模板引擎各個(gè)實(shí)例的關(guān)系是一種組合依賴但又具備基本的封裝和隔離的狀態(tài)說了這么多。
轉(zhuǎn)載于:https://www.cnblogs.com/moneyss/p/7060641.html
總結(jié)
- 上一篇: linux内核I2C子系统学习(三)
- 下一篇: SpringMVC中@GetMappin