KO的逆襲-HTML自訂元素上場
2 | 10,451 |
先看展示:
這是一個很簡單的 MVVM 繫結示範,模擬網頁常見「給幾顆星」的評分機制。上方透過 click 事件修改observable,同時繫結到下拉選單及Score=文字顯示。同一 observable 繫結到兩個 UI 元素的情境對KO來說是小菜一碟,不足掛齒,但 HTML 裡有玄機:
<div>
<star-rating params="value: score"></star-rating>
</div>
<select data-bind="value: score">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</select>
Score=<span data-bind="text: score"></span>
咦?<star-rating param="value: score"></star-rating>? 有妖術!莫非 KO 追上 NG,樣開始支援自訂 Directive?
先前評估 KO 與 NG,我給「自訂Directive」很高的評價。在網頁使用<my-element>標籤置入自訂元素,能大幅提升 HTML 簡潔性並有利 UI 邏輯分割與重覆利用,是很讓人動心的特色,也是讓我傾向 NG 的關鍵之一。上個月, Knockout.js 釋出 3.2.0 版,最大亮點便是加入元件(Component)概念,在我心中,此一改變讓版號直接跳上4.0都不為過!XD
來看如何在KO自訂HTML元件,方法很簡單,透過 ko.components.register() 註冊元件名稱,並提供 ViewModel 及 Template 定義,搞定!(完整程式及線上展示)
ko.components.register('star-rating', {
viewModel: function(params) {
var self = this;
//Data
self.value = params.value;
self.options = [1,2,3,4,5];
//Behaviors
self.pick = function(n) { self.value(n); }.bind(self);
},
template:
'<div data-bind="foreach: options" class="stars">' +
'<span data-bind="click: $parent.pick,
text: $data<=$parent.value()?\'★\':\'☆\'"></span>' +
'</div>'
});
每個元件自成一個獨立 ViewModel,因此在同一網頁上重複加入多個元件也不致打架。ViewModel 在建構時能透過 params 參數取得外部傳入的固定值參數、observable、observableArray、computed,將其轉為元件 ViewModel 屬性(例如:self.value = params.value),在 Template中透過 data-bind="text: value",便能與外部傳入的 observable 建立繫結。Template 與 ViewModel 的整合方式跟一般的 KO 做法完全相同,已有 KO 經驗的開發者幾乎不需學習就能上手。
KO 官網已新増一個章節介紹元件,是最權威的文件來源,在此整理幾則重點:
- 除了直接寫成<my-element params="…">,KO 也支援<div data-bind="component: { name: 'my-element', params: … }">寫法。但說真的,有龍蝦,誰要吃小魚乾呢? XD
- <my-element>的寫法在 IE6/7/8 受限制,必須確保 ko.components.register("my-element", …) 已執行,網頁才能出現<my-element>,否則要加入document.createElement('my-element'); 避免 my-element 元素被無視。
- 不可以寫成<my-element params="…" />(Self-Closing),一定要拆成<my-element></my-element>,這是 HTML 規格限制。(這點跟<script></script>一樣,想到之前踩到的釘子,腳彷彿還在痛)
- KO 會自動將 params="boo: someObservable() + 1" 這種摻雜 observable 的計算型參數轉成 observableExpression,當someObservable 改變時連動 boo 以反應到 UI。
- 針對複雜元件,可以透過 createViewModel 函式動態決定 ViewModel。
- 除了以字串方式指定 Template,也可將 Template 放進 DOM 元素(<template>或<script type="text/template">)並寫成template: { element: 'my-component-template' },或是更進一步將元件的 ViewModel JavaScript 及 Template HTML 分別存成獨立 .js 與 .html 檔案,配合 AMD 動態載入。
得知 KO 的新功能後,二話不說,馬上將專案一個重複出現 N 次的 HTML 片段改寫成自訂元素,看著修改後變清爽的 HTML,我決定再次評估 KO 與 NG 的選擇考量:
KO 比較輕巧,偏向 Library 而非 Framework,如果對架構已有自已的規劃,不想受限 NG 複雜且龐大的框架,KO 比較容易相處好上手,對一些輕量級前端應用較有彈性。(但別誤會,KO 絕對能拿來寫大型 SPA,結合 Grunt、Gulp、Jasmine、Karma 實現自動測試及CI。Steven Sanderson前陣子在NDC 2014的演講有個完整展示,前10分鐘有個 Window 8 動態磚風格的網頁操作,很酷!)
NG 是前端主流架構的情勢短期內不會改變(但長期而言,Who Knows?),採用 NG 將享受較多的資源,而其較嚴謹的架構及大量運用設計模式將有助於開發人員學習建立中大型系統必要的知識與技巧。(但相對的,對初學者而言門檻也較高)
若回歸到只想實現 JavaScript 端 MVVM 的場合,雖然 NG 不需宣告 observable 就能建立繫結的概念很吸引人,但探究其背後是靠反覆重算的 Direty Check 實現,以我個人觀點,倒是偏好用 obeservable 及computed 自己精準掌控相依關聯,從另個角度來看,宣告 observable 就像 Strong Type 一樣,付出代價也會享受到好處。在 KO 加入 HTML 自訂元素功能後,我想在純粹 MVVM 應用上,KO 還是一個很棒的選擇。
[KO系列]
Comments
# by REX
自訂元素固然很方便 可以強調模組化概念 但我對置換HTML有疑問 因為破壞已經成形的樹結構 對效能上來說會有一定程度的影響(重繪樹結構) 假使整個HTML版都是模組化TAG 不知道有沒有什麼方式是可以兼顧到效能表現? 除了少用
# by Jeffrey
to REX, Good Point!之前沒想到這一點。依我個人想法,基於其有利程式開發及維護的考量,值得拿效能方面的些許損耗做交換,就像明知Native JavaScript會比jQuery有更好的效能表現,除非在特殊情境裡效能落差大到造成困擾,否則我還是會優先用jQuery。