后端的動態(tài)模板
Java后端通常會使用ftl(freemarker template language)模板文件來動態(tài)返回前端頁面。這個工作,通常還可以用jsp、php文件來實現(xiàn)。但這些動態(tài)模板的實現(xiàn),通常是在已有的html文件上對特定的需要做動態(tài)處理的部分做改寫。這對小項目來講沒什么不對??扇绻愕捻撁鏀?shù)量足夠多,維護它們將成為一件異常困難的事情。
Nodejs大前端技術(shù)
在目前的大前端技術(shù)棧下,Nodejs的各種框架讓前端開發(fā)變得規(guī)矩不少。我的一個理解是,傳統(tǒng)前端的html+css+js的技術(shù)棧的最大問題在于其模塊化、組織能力像是一個教學(xué)語言,應(yīng)有的語句控制和代碼復(fù)用的技術(shù),都顯得蒼白無力。
就html的編寫來講,幾乎不存在一種類似函數(shù)的復(fù)用方式,能夠簡化重復(fù)的UI component的生成。你只能不斷地去寫一些重復(fù)的、雜亂的代碼。整體上來講,這不僅難以做后期的維護,也無法輕易地看懂其間的代碼邏輯。
一句話來講,這些代碼非常類似于機器代碼或者匯編代碼。沒有高級語言的精準控制和抽象層去對代碼做宏觀把控。
Pugjs是一個很好的html預(yù)處理項目。它的基本想法是:
不要去直接編寫“底層”的html代碼,而是用自己定義的一套語法去編寫
pug文件。
通過這個pug文件去生成出html代碼。特別的,在它的語法中,你不必再寫一大堆的尖括號和與前后呼應(yīng)的tag。如同Python,僅僅依靠代碼的對齊方式,就可以自動識別相應(yīng)的作用域范圍。例如
<div>
<ul>
<li> First tip </li>
<li> Second tip </li>
<li> Third tip </li>
</ul>
</div>
這樣語義簡單、語法繁瑣的一堆代碼,在pug下可以簡化為
div
ul
li First tip
li Second tip
li Third tip
但這還不是最誘人的技術(shù),因為這無非是加入了一些語法糖。最為誘人的是pug提供的函數(shù),它能夠定義一個函數(shù)去生成某個組件。
例如,如果你需要定義一組table,每個table僅僅是表頭或者其中一部分的數(shù)據(jù)不一樣,你該如何處理?傳統(tǒng)的方式當然是復(fù)制粘貼一堆模板代碼,然后一個個地去修改里面的數(shù)據(jù)。
而pug的處理方式就要好太多,完全符合將數(shù)據(jù)和代碼分離的思想。定義函數(shù):
mixin leftbox-gen(dataObj)
table.table
thead
tr
th(scope="col") #{dataObj.title}
tbody
each area in dataObj.areas
tr
td
.box-title #{area.name}
ul
each subarea in area.subareas
li
a(id=subarea.id, href=subarea.url) #{subarea.name}
這樣就可以根據(jù)通過定義json格式的dataObj去引用函數(shù):
+leftbox-gen(cs_leftbox_data)
你通過不同的json數(shù)據(jù),就能夠生成不同的table出來。這就實現(xiàn)了代碼的模塊化和以及數(shù)據(jù)和業(yè)務(wù)代碼的分離。要做出新的table component,你只需要改變數(shù)據(jù)就可以了。
這樣的實現(xiàn)方式在別的高級語言中是很常見的,但是在傳統(tǒng)的前端代碼中,這常常難以見到。原因就在于,html代碼更像是沒有抽象層的機器代碼,只是一大堆的實際操作,而缺少抽象層的高效管理。
前端預(yù)處理和后端動態(tài)模板的結(jié)合
像pug這樣優(yōu)秀的工具,如果能夠用來管理后端的ftl模板當然會相當合適。優(yōu)秀的語法糖、代碼模塊化、數(shù)據(jù)和業(yè)務(wù)邏輯的分離,實在是相當誘人的選擇。
但這樣的理念真要實施在生產(chǎn)代碼中,特別是用來重構(gòu)已有的legacy code時,就不大容易了。
例如,pug只能生成html代碼,且生成出來的位置通常是在一個統(tǒng)一的地方???code>ftl代碼卻分散在各個不同Java工程的不同目錄之下。這兩者很難統(tǒng)一到一起。
或許一個直接的想法是,不如直接把所有的ftl都放到一個地方,這樣就不用把模板語言分散到各個不同項目的不同文件夾里,而難以維護。
但這種方案帶來的一個麻煩是,如果真的把后端的ftl文件挪動了位置,那么你后端代碼的接口部分就不得不做修改。而這樣的接口部分其數(shù)量并不少。既要做出大量的修改,還要保證它們的正確性,并不是一件輕松的事情。
經(jīng)過大量的思考和嘗試,我得出的一個解決方案是:
使用
Pugjs生成出統(tǒng)一的ftl文件,放在同一個公共資源文件夾下。讓每一個具體項目下的ftl文件中,直接include這個公共資源文件夾中ftl內(nèi)容。
這種做法的一個精妙之處是:它將ftl文件當作函數(shù)接口來使用。后端Java代碼調(diào)用ftl文件可以看作是函數(shù)調(diào)用。而函數(shù)實現(xiàn)并不需要直接放在這個ftl文件里,而是可以放在別的地方做引用。這就把實現(xiàn)和調(diào)用部分,通過一個單獨的文件分離開了。
這里雖然處理的是后端模板文件和前端的一個結(jié)合,但其思想可以利用在別的地方。如果一個模板文件具備了include功能,便可以把模板文件本身當作接口,從而將實現(xiàn)與定義分離。