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