當前位置:編程學習大全網 - 源碼下載 - 如何選擇Web前端模板引擎(推薦)

如何選擇Web前端模板引擎(推薦)

本篇文章給大家帶來的內容是關於如何選擇Web前端模板引擎(推薦),有壹定的參考價值,有需要的朋友可以參考壹下,希望對妳有所幫助。

模板引擎負責組裝數據,以另外壹種形式或外觀展現數據。 瀏覽器中的頁面是 Web 模板引擎最終的展現。

無論妳是否直接使用模板引擎,Web 模板壹直都在,不在前端就在後端,它的出現甚至可以追溯到超文本標記語言 HTML 標準正式確立之前。

服務器端的模板引擎

我所知道最早的 Web 模板引擎是 PHP,它正式誕生於 1997 年,工作在服務器端。讓我們看看 PHP 官方的 intro-whatis:

HPer 普遍贊同 PHP 本身就是最天然、原生的 PHP 模板引擎,因為她本來就是。在 PHP 的世界裏多次出現過再包裝的模板引擎,著名的有 smarty。

其它服務器端語言很多都有 HTML 模板引擎,比如 JSP、mustache。

毫無疑問,這些服務器端模板引擎最終生成的結果是 HTML(XML) 字符串,處理流程邏輯使用宿主語言本身的語法實現。

它們的***同特征:HTML 只是個字符串, 最終結果可能還需要類似 Tidy 這樣的清潔或修正驗證工具。

這裏提出壹個問題:二次封裝的 smarty 有存在的必要麽?

瀏覽器端的模板引擎

我所知道最早的前端模板引擎是 jCT,它托管於 Google Code,誕生於 2008 年,宿主語言是 JavaScript,工作在瀏覽器中。

今天在 OSC 搜索 JavaScript 模板引擎妳會得到 100+ 個結果,下邊列舉壹些:

輕量度:tpl.js、T.js

認知度:arttemplate、mustache.js、doT.js、handlebars.js、pug

DOM-tree-based:domTemplate、transparency、plates

VDOM-based:htmltemplate-vdom、virtual-stache、html-patcher

流行框架:Vue.js、ReactJS、riot

Real-DOM:PowJS

它們的***同特征:全都支持插值。

這裏還有 templating-engines 受歡迎度的對比,甚至 best-javascript-templating-engines 投票及正反方的理由。

如何選擇

我認為存在即合理,每個引擎、框架總有可取之處,至少在妳的應用裏,在某個時代,所以本文不會評論某個引擎哪壹點不好,那樣是不客觀的。現在回答前邊提到的問題:smarty 有存在的必要麽?我的答案是:有。理由很簡單,看給誰用、看大背景。對於前後端沒有分離的應用,或前端人員對後端語言不夠熟悉,或因崗位職責需要,那麽前端人員掌握壹種比較通用的模板語法(語言)是現實的,反之讓 PHPer 自己去使用 smarty 那就太浪費技能了。

下面是通常意義上的引擎選擇建議:

前提,選擇的引擎能滿足數據渲染需求,且不和現有依賴沖突,如果妳已經非常熟悉某個引擎,那妳已經有答案了。

是壹次性的項目需求麽? 是的話直接選擇輕量的,學習復雜度最低的。 是要做組件開發麽?

引擎支持預編譯結果,不必每次都實時編譯麽?

要跨平臺麽? 有官方提供支持的,首選類 React-JSX 的引擎或純粹的 VDOM 引擎。

選擇學習或維護復雜度最低的,眾所周知,開發者對調試的時間超過寫代碼的時間深惡痛絕。

最後才是性能對比,性能對比是壹件非常細致的工作,他人的對比結果不壹定符合妳的場景。

我認為應該弱化語法風格的對比,偏好是沒有可比性的,壹些語法甚至有特殊的背景原因。

為什麽最後才是性能對比?

性能的確很重要,但如果性能還沒有影響到妳的應用體驗度,那就忽視它。很難真實地模擬應用場景,通常只有通過真實場景來檢驗,目前的測試工具還達不到這種效果。

前述問題有些有固定答案,下面討論余下的問題:如何考慮組件開發、支持預編譯、復雜度?

組件開發

進行組件開發已經不再是選擇模板引擎的問題了,這是生態環境選擇的問題。如果妳的應用需要更快地完成,那麽時間點是第壹位的,就選擇流行框架,有足夠多的組件讓妳使用或參考。如果妳的應用有獨立的生態環境,需要技術選型以便長期維護,那繼續看下文。

預編譯

預編譯應該具備:

編譯結果在目標環境中不再需要編譯過程。

編譯結果可調試性,這意味著結果應該包含原生 ECMAScript 代碼,而不是純粹的數據描述。

大家都知道 React-JSX 是支持預編譯的,官方的說法是 React Without JSX,即總是 build 過的。

壹些基於字符串處理的引擎也支持預編譯。如果妳需要預編譯,建議拋棄編譯結果依然是基於字符串拼接的引擎,那樣還不如不預編譯,那是 HTML5 未被廣泛支持之前的技術手段。

至少也要有類似 React-JSX 這樣的編譯結果才具有可調試性。備註:Vue.js 支持多種模板引擎,可達到同樣的效果。

原 ReactJS 代碼,其中用到了 Web Components 技術:

class HelloMessage extends React.Component {

render() {

return <p>Hello <x-search>{this.props.name}</x-search>!</p>;

}

}編譯後:

class HelloMessage extends React.Component {

render() {

return React.createElement(

"p",

null,

"Hello ",

React.createElement(

"x-search",

null,

this.props.name

),

"!"

);

}

}不少 VDOM 引擎也可以編譯類似效果,比如 htmltemplate-vdom。

<script>

var id = 3;

var env = {

people: [

{

id: 'id1',

name: 'John',

inner: [{ title: 'a1' }, { title: 'b1' }],

city: 'New York',

active: true

},

{

id: 'id2',

name: 'Mary',

inner: [{ title: 'a2' }, { title: 'b2' }],

city: 'Moscow'

}

],

githubLink: '/agentcooper/htmltemplate-vdom',

itemClick: function(id) {

env.people.forEach(function(person) {

person.active = String(person.id) === String(id);

});

loop.update(env);

}

// Omitted ....

};

</script>復雜度

很難用唯壹的標準去評判兩個引擎哪個復雜度低,這是由使用者的思維模式不同造成的。例如前邊列出的引擎在使用上以及預編譯結果上的區別,不同使用者感觸是不同的,這正是不同引擎存在的合理性、價值性。

有的使用者認為這個應用場景有字符串模板就滿足了需求,輕量夠用。

有的使用者認為字符串拼接技術的模板引擎不夠強壯,不夠時代感。

有的使用者認為OOP 夠理性,夠邏輯,夠抽象。

有的使用者認為原生 HTML 才叫前端。

有的使用者認為 VDOM 適用性更廣。

這些評判都有各自的理由,著眼點不同,標準也就不同了。但是我們還是可以從它們的***性去考慮它們的復雜度。

字符串類模板通常都很輕量,不在本節討論範圍之內。對於非字符串模板復雜度評判的***性標準是什麽?我認為,可以考量數據綁定的復雜度。

本文所指的數據綁定不只是插值,還包括上下文以及事件,甚至是整個運行期的宿主環境。

事實上至少需要達到 VDOM 級別的引擎才具有這種能力,因為通過 VDOM 可以映射到真實的 DOM 節點。

大概有幾種模式(組合):

1.入口參數是個 Object,模板中的變量 x 是該對象的 .x 屬性,例:virtual-stache-example

2.特定語法或屬性,比如:Vue.js 的 ...,屬性 computed、methods

3.抽象的語義化屬性,比如:Vue.js 的 active 這個詞適用於多種場景,容易理解且不產生歧義

4.不負責綁定,需要使用者非常熟悉原生方法,用原生方法進行綁定,比如:PowJS

這些模式只是理論方面的,通常是模板引擎設計者要解決的問題。對於使用者來說不如直接問:

1.可以在 HTML 模板中直接寫最簡單的 console.log(context) 來調試麽?

2.可以在多層 DOM 樹綁定或傳遞不同的上下文參數麽?

3.可以在多層 DOM 樹內層向上訪問已經生成的 Node 麽?

模板引擎團隊會給妳正確的解決辦法,但通常和問題字面描述的目標有所差異。我覺得這就是妳評判選擇的關鍵,妳對官方給出的正確方法的認可度。

嵌入到 DOM 中

嵌入到 HTML 中

PowJS 是這麽實現的:

實現模板必須要實現的指令

預編譯輸出原生 ECMAScript 代碼

模板語法結構與 ECMAScript 函數寫法壹致

最終,寫 PowJS 模板就像在寫 ECMAScript 函數。

GoHub index 中的寫法

<template>

<details func="repo" param="data" if="is.object(data.content)&&!sel(`#panel details[sha='${data.sha}']`)"

open

let="ctx=data.content"

sha="{{data.sha}}"

origin="{{ctx.Repo}}"

repo="{{data.owner}}/{{data.repo}}"

subdir="{{ctx.Subdir||''}}"

filename="{{ctx.Filename}}"

render=":ctx"

do="this.renew(sel(`#panel details[repo='${data.owner}/${data.repo}']`))"

break

>

<summary>{{ctx.Description}}</summary>

<p if="':';" each="ctx.Package,val-pkg">

<p title="{{pkg.Progress}}: {{pkg.Synopsis}}">{{pkg.Import}}</p>

</p>

</details>

<dl func="list" param="data"

if="!sel(`#panel details[sha='${data.sha}']`)&&':'||'';"

each="data.content,data.sha,val-rep"

do="this.appendTo(sel('#panel'))">

<details sha="{{sha}}" repo="{{rep.repo}}">

<summary>{{rep.synopsis}}</summary>

</details>

</dl>

</template>多數模板引擎都會實現 if 、each 這些指令,上面的 PowJS 模板中還有:

全局對象 is、sel

模板(函數)命名 repo 、list

模板(函數)入口形參 data

自定義局部變量 ctx

下層模板(函數)形參推導 data.sha->sha

遍歷值到下層模板形參推導 (ctx.Package,val-pkg)->pkg 、(data.content,val-rep)->rep

DOM 節點操作 this.renew、 this.appendTo,這直接渲染到頁面 DOM 樹

流程控制 break

偽節點 if="':';",渲染時根本不生成 p 節點,它是個偽節點,相當於塊代碼符號 "{}"

關鍵是整個模板結構,指令語義和 ECMAScript 函數完全壹致:

沒有數據綁定,妳寫的是 ECMAScript 函數,傳參數好了,要什麽綁定

沒有事件綁定,每個節點都是真實存在的,直接寫 addEventListener 就好了

要調試,隨便找個 do 或 if 或 let 插入 _=console.log(x), 就好了,逗號表達式幾乎可以無縫插入所有原生語句

所有的業務邏輯都是使用者自己寫的,PowJS 只負責把他們粘合成壹個函數

導出視圖是 ECMAScript 源碼,下圖截取自演示 My Folders

那麽 PowJS 是最終的選擇麽?PowJS 的理念是原生性,原生的 DOM,原生的 ECMAScript。

原生也同樣是 PowJS 的問題所在,不是所有的使用者都喜歡原生,我相信有的使用者更喜歡更抽象風格,他們眼中的原生總是帶了點 "原始"。

原生意味著妳可以擴展,引入其它 library 進行搭配,但 PowJS 永遠不會出現 define setter/getter實現的 watcher,那超出了模板引擎的範圍,如果有那壹定是獨立的項目。

最後,我的觀點依然是:妳的需求才是選擇模板的關鍵,適合妳的才是好的。

  • 上一篇:啞源代碼
  • 下一篇:張偉接受媒體采訪
  • copyright 2024編程學習大全網