前端模板引擎需要有開發時的透明性
#透明性即指我在搭建好開發環境後,隨手寫程式碼隨手刷新瀏覽器就能看到最新的效果,而不需要額外執行任何指令或有任何的等待過程。
所以一切都依賴編譯過程的模板引擎並不適合前端使用,編譯只能是模板引擎的特性,而不能是使用的前提
更嚴格地說,使用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去異步獲取這一模組,然後將常量作為數據傳遞給模板引擎,但這是一種業務與視圖相對耦合的玩法,出於強迫症我並不覺得這是一個漂亮的設計,所以我們希望前端範本引擎要支援不同的開發模式
前端發展至今,有許多不同的開發模式,例如:在大型的前端項目,特別是在單頁式的項目中,會有完全未知個數的模板片段同時存在,如果這些片段是帶有名稱(出於復用的考慮)的,就很容易造成名稱上的衝突
對於同一層級的邏輯(如大家都是業務層程式碼,或大家都是控制層程式碼),名稱衝突是可以透過一些開發時的約定來解決的。但不同層之間,由於封裝性的要求,外部不應該知道一些僅內部使用的片段的名稱,此時如果不幸有名稱與其它層有衝突,會讓情況變得比較麻煩,這類問題甚至都不容易跟踪,往往會導致大量的精力和時間的浪費
因此,一個好的模板引擎應該是多實例的,且不同實例間應該相互具備隔離性,不會出現這種不可預期的衝突
將這個主題再往深地研究,就會發現單純的隔離是不夠的,不同層間除了不衝突的需求,同樣還有片段復用的需求,我們還會需要不同模板實例間可以開放一些固定的片段共享,因此模板引擎各個實例的關係是一種組合依賴但又具備基本的封裝和隔離的狀態說了這麼多。
以上是關於前端模板引擎的問題詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!