組件綁定將指定的組件注入到元素中,并且可選地將參數傳遞給它。
本節目錄
- 一個例子
- API
- 組件生命周期
- 備注1:僅限模板組件
- 備注2:使用沒有容器元素的組件
- 備注3:將標記傳遞給組件
- 處置和內存管理
一個例子
First instance, without parameters
Second instance, passing parameters
UI源碼:
<h4>First instance, without parameters</h4> <div data-bind='component: "message-editor"'></div> <h4>Second instance, passing parameters</h4> <div data-bind='component: { name: "message-editor", params: { initialText: "Hello, world!" } }'>
</div>
視圖模型源碼:
ko.components.register('message-editor', { viewModel: function(params) { this.text = ko.observable(params && params.initialText || ''); }, template: 'Message: <input data-bind="value: text" /> ' + '(length: <span data-bind="text: text().length"></span>)' }); ko.applyBindings();
注意:在更現實的情況下,通常從外部文件加載組件視圖模型和模板,而不是將它們硬編碼到注冊中。
API
有兩種方法使用組件綁定:
快速語法:
如果你只傳遞一個字符串,它被解釋為一個組件名稱。 然后注入命名的組件,而不向其提供任何參數。 例:
<div data-bind='component: "my-component"'></div>
也可以將監控屬性作為組件名稱。 在這種情況下,如果監控屬性值更改,組件綁定將處理舊組件實例,并注入新引用的組件。 例:
<div data-bind='component: observableWhoseValueIsAComponentName'></div>
完整語法:
要向組件提供參數,請傳遞具有以下屬性的對象:
name
— 要注入的組件的名稱。 同樣,這可以是監控屬性。params
—將被傳遞給組件的對象。 通常,這是一個包含多個參數的鍵值對象,通常由組件的viewmodel構造函數接收。
例如:
<div data-bind='component: { name: "shopping-cart", params: { mode: "detailed-list", items: productsList } }'> </div>
組件生命周期
當組件綁定注入組件時,
-
請求您的組件加載器提供viewmodel工廠和模板
- 可以查閱多個組件加載器,直到第一個識別組件名稱并提供視圖模型/模板。 此過程僅對每個組件類型發生一次,因為Knockout在內存中緩存生成的定義。
- 默認組件加載器根據您注冊的內容提供viewmodels /template。 如果適用,這是從AMD加載器請求任何指定的AMD模塊的階段。
-
組件模板被克隆并注入到容器元素中
任何現有內容都將被刪除并丟棄。
-
如果組件有一個viewmodel,它被實例化
如果viewmodel是作為構造函數給出的,這意味著Knockout調用新的YourViewModel(params)。
如果viewmodel作為createViewModel工廠函數給出,Knockout callscreateViewModel(params,componentInfo),其中componentInfo.element是尚未綁定的未綁定模板的元素。
這個階段總是同步完成(構造函數和工廠函數不允許是異步的),因為每次組件被實例化時都會出現這種情況,如果涉及等待網絡請求,性能將是不可接受的。
-
viewmodel被綁定到視圖
如果組件沒有viewmodel,則視圖將綁定到您提供給組件綁定的任何參數。
-
組件處于活動狀態
現在組件正在運行,并且可以在需要時保持在屏幕上。
如果傳遞給組件的任何參數是監控屬性,則組件當然可以觀察到任何改變,或者甚至回寫修改的值。 這是它如何能夠干凈地與其父進行通信,而不是將組件代碼緊密地耦合到使用它的任何父進程。
-
組件被拆卸,并且視圖模型被刪除
If如果組件綁定的名稱值可觀察地改變,或者如果封閉的控制流綁定導致容器元素被移除,則在從DOM移除容器元素之前調用視圖模型上的任何dispose函數。 參見本節:處置和內存管理。
注意:如果用戶導航到完全不同的網頁,瀏覽器會執行此操作,而不會要求頁面中運行的任何代碼進行清理。 所以在這種情況下不會調用dispose函數。 這是正常的,因為瀏覽器會自動釋放所有使用的對象使用的內存。
通常,這是一個異步過程。 它可能涉及對服務器的請求。 對于API一致性,Knockout默認確保加載過程作為異步回調完成,即使組件已經加載并緩存在內存中。 有關更多信息以及如何允許同步加載,請參閱上一節的控制同步/異步加載。
備注1:僅限模板組件
組件通常有viewmodels,但它們不一定必須。 組件只能指定一個模板。
在這種情況下,組件視圖所綁定的對象是您傳遞給組件綁定的params對象。 例:
ko.components.register('special-offer', { template: '<div class="offer-box" data-bind="text: productName"></div>' });
...可以注入參數:
<div data-bind='component: { name: "special-offer-callout", params: { productName: someProduct.name } }'></div>
或者,更方便地,作為自定義元素:
<special-offer params='productName: someProduct.name'></special-offer>
備注2:使用沒有容器元素的組件
有時,您可能想要將一個組件注入到視圖中,而不使用額外的容器元素。 您可以使用基于注釋標簽的無容器控制流語法。 例如,
<!-- ko component: "message-editor" --> <!-- /ko -->
...或傳遞參數:
<!-- ko component: { name: "message-editor", params: { initialText: "Hello, world!", otherParam: 123 } } --> <!-- /ko -->
<!-- ko -- >和<!-- / ko -- >注釋作為開始/結束標記,定義一個包含標記的“虛擬元素”。 Knockout理解這個虛擬元素的語法,并綁定,就像你有一個真正的容器元素。
備注3:將標記傳遞給組件
您附加組件綁定的元素可能包含進一步的標記。 例如,
<div data-bind="component: { name: 'my-special-list', params: { items: someArrayOfPeople } }"> <!-- Look, here's some arbitrary markup. By default it gets stripped out and is replaced by the component output. --> The person <em data-bind="text: name"></em> is <em data-bind="text: age"></em> years old. </div>
雖然此元素中的DOM節點將被刪除,并且不會默認綁定,但它們不會丟失。 相反,它們被提供給組件(在這種情況下,my-special-list),它可以將它們包括在它希望的輸出中。
如果要構建表示“容器”UI元素的組件(如網格,列表,對話框或標簽集),這需要注入并將任意標記綁定到公共結構中,這將非常有用。 它也可以在沒有自定義元素的情況下使用上面顯示的語法。
處置和內存管理
您的viewmodel類可能具有dispose函數。 如果實現了,當組件被刪除并從DOM中刪除時,Knockout會調用它(例如,因為相應的項從foreach中刪除,或者如果綁定變為false)。
您必須使用dispose釋放任何本質上不可回收的資源。 例如:
setInterval
回調將繼續觸發,直到顯式清除。- 使用clearInterval(handle)來停止它們,否則你的viewmodel可能被保存在內存中。
- ko.computed屬性繼續從其依賴關系接收通知,直到明確處置。
- 如果一個依賴是一個外部對象,那么一定要使用.dispose()在computed屬性,否則它(可能還有你的viewmodel)將被保存在內存中。 或者,考慮使用pureComputed以避免手工處置的需要。
- 監控屬性訂閱將繼續運行,直到明確被處理。
- 如果你訂閱了一個外部的observable,一定要使用.dispose()在訂閱,否則回調(可能還有你的viewmodel)將被保存在內存中。
- 外部DOM元素上手動創建的事件處理程序(如果在createViewModelfunction中創建)(甚至在常規組件視圖模型內部,盡管適合您不應該使用的MVVM模式)必須刪除。
- 當然,您不必擔心在視圖中釋放由標準Knockout綁定創建的任何事件處理程序,因為KO會在刪除元素時自動注銷它們。
例如:
var someExternalObservable = ko.observable(123); function SomeComponentViewModel() { this.myComputed = ko.computed(function() { return someExternalObservable() + 1; }, this); this.myPureComputed = ko.pureComputed(function() { return someExternalObservable() + 2; }, this); this.mySubscription = someExternalObservable.subscribe(function(val) { console.log('The external observable changed to ' + val); }, this); this.myIntervalHandle = window.setInterval(function() { console.log('Another second passed, and the component is still alive.'); }, 1000); } SomeComponentViewModel.prototype.dispose = function() { this.myComputed.dispose(); this.mySubscription.dispose(); window.clearInterval(this.myIntervalHandle); // this.myPureComputed doesn't need to be manually disposed. } ko.components.register('your-component-name', { viewModel: SomeComponentViewModel, template: 'some template' });
不必嚴格地需要僅僅依賴于相同viewmodel對象的屬性來處理計算和訂閱,因為這僅創建了JavaScript垃圾收集器知道如何釋放的循環引用。 然而,為了避免不必記住哪些事情需要處理,你可能更喜歡在任何可能的地方使用pureComputed,并且顯式地處置所有其他計算/訂閱,無論技術上是否必要。
文章列表