KnockoutJS 3.X API 第六章 組件(5) 高級應(yīng)用組件加載器
無論何時(shí)使用組件綁定或自定義元素注入組件,Knockout都將使用一個或多個組件裝載器獲取該組件的模板和視圖模型。 組件加載器的任務(wù)是異步提供任何給定組件名稱的模板/視圖模型對。
本節(jié)目錄
- 默認(rèn)組件加載器
- 組件加載器實(shí)用函數(shù)
- 實(shí)現(xiàn)自定義組件加載器
- 可以實(shí)現(xiàn)的功能
getConfig(name, callback)loadComponent(name, componentConfig, callback)loadTemplate(name, templateConfig, callback)loadViewModel(name, templateConfig, callback)
- 注冊自定義組件加載器
- 控制優(yōu)先級
- 調(diào)用順序
- 可以實(shí)現(xiàn)的功能
- 示例1:設(shè)置命名約定的組件加載程序
- 示例2:使用自定義代碼加載外部文件的組件加載器
- 注意1:自定義組件加載器和自定義元素
- 注意2:與browserify集成
默認(rèn)組件加載器
內(nèi)置的默認(rèn)組件加載器ko.components.defaultLoader基于組件定義的中心“注冊表”。 它依賴于您明確注冊每個組件的配置,然后才能使用該組件。
可參考第六章第一節(jié)。
組件加載器實(shí)用函數(shù)
以下函數(shù)讀取和寫入默認(rèn)組件加載器的注冊表:
ko.components.register(name, configuration)- 注冊組件
ko.components.isRegistered(name)- 如果具有指定名稱的組件已注冊,則返回true;否則為假。
ko.components.unregister(name)- 從注冊表中刪除指定的組件?;蛘呷绻麤]有注冊這樣的組件,什么都不做。
以下函數(shù)在注冊的組件加載器的完整列表中工作(不僅是默認(rèn)加載器):
ko.components.get(name, callback)- 依次查詢每個注冊的加載器(默認(rèn)情況下,只是默認(rèn)加載器),找到第一個為命名的組件提供viewmodel /模板定義,然后調(diào)用回調(diào)來返回比viewmodel / template聲明。如果沒有注冊的裝載器知道這個組件,則調(diào)用回調(diào)(null)。
ko.components.clearCachedDefinition(name)- 通常,Knockout對每個組件名稱查詢加載器一次,然后緩存生成的定義。這確保了可以非??焖俚貙?shí)例化大量組件。如果要清除給定組件的緩存條目,請調(diào)用此方法,然后在下次需要該組件時(shí)再次查詢加載程序。
此外,由于ko.components.defaultLoader是組件加載器,它實(shí)現(xiàn)以下標(biāo)準(zhǔn)組件加載器函數(shù)。您可以直接調(diào)用這些方法,例如,作為自定義加載器實(shí)施的一部分:
ko.components.defaultLoader.getConfig(name, callback)ko.components.defaultLoader.loadComponent(name, componentConfig, callback)ko.components.defaultLoader.loadTemplate(name, templateConfig, callback)ko.components.defaultLoader.loadViewModel(name, viewModelConfig, callback)
實(shí)現(xiàn)自定義組件加載器
如果要使用命名約定而不是顯式注冊來加載組件,則可能需要實(shí)現(xiàn)自定義組件加載器。 或者,如果您想使用第三方“加載器”庫從外部位置獲取組件視圖模型或模板。
可以實(shí)現(xiàn)的功能
自定義組件加載器只是一個對象,其屬性是以下函數(shù)的任意組合:
getConfig(name, callback)
定義如下: 您希望基于名稱以編程方式提供配置,例如,實(shí)現(xiàn)命名約定。
如果聲明,Knockout將調(diào)用此函數(shù)為每個正在被實(shí)例化的組件獲取一個配置對象。
- 要提供配置,請調(diào)用回調(diào)(componentConfig),其中componentConfig是加載器或任何其他加載器上的loadComponent函數(shù)可以理解的任何對象。 默認(rèn)加載器只提供使用ko.components.register注冊的任何對象。
- 例如,一個componentConfig像{template:'someElementId',viewModel:{require:'myModule'}}可以被默認(rèn)加載器理解和實(shí)例化。
- 您不限于以任何標(biāo)準(zhǔn)格式提供配置對象。 只要loadComponent函數(shù)理解它們,就可以提供任意對象。
- 如果你不希望你的加載器提供一個命名組件的配置,那么callcallback(null)。 然后,Knockout將按順序查詢?nèi)魏纹渌缘难b載器,直到提供非空值。
loadComponent(name, componentConfig, callback)
定義如下: 您想要控制組件配置的解釋方式,例如,如果您不想使用標(biāo)準(zhǔn)的viewModel /模板對格式。
如果聲明,Knockout將調(diào)用此函數(shù)將componentConfig對象轉(zhuǎn)換為viewmodel /模板對。
-
要提供一個viewmodel /模板對,請調(diào)用callback(result),其中result是具有以下屬性的對象:
template- 必需。 DOM節(jié)點(diǎn)數(shù)組createViewModel(params, componentInfo)- 可選。 稍后將調(diào)用的函數(shù)以為此組件的每個實(shí)例提供一個viewmodel對象
-
如果你不希望你的加載器為給定的參數(shù)提供一個viewmodel /模板對,那么callcallback(null)。 然后,Knockout將按順序查詢?nèi)魏纹渌缘难b載器,直到提供非空值。
loadTemplate(name, templateConfig, callback)
定義如下: 您想要使用自定義邏輯為給定模板配置提供DOM節(jié)點(diǎn)(例如,使用ajax請求通過URL提取模板)。
默認(rèn)組件加載器將在聲明它的任何注冊加載器上調(diào)用此函數(shù),將組件配置的template部分轉(zhuǎn)換為DOM節(jié)點(diǎn)數(shù)組。 然后,為組件的每個實(shí)例緩存和克隆節(jié)點(diǎn)。
templateConfig值只是來自任何componentConfig對象的template屬性。 例如,它可能包含“some markup”或{element:“someId”}或自定義格式,如{loadFromUrl:“someUrl.html”}。
-
要提供DOM節(jié)點(diǎn)的數(shù)組,請調(diào)用回調(diào)(domNodeArray)。
-
如果您不希望您的加載程序?yàn)榻o定的參數(shù)提供模板(例如,因?yàn)樗荒茏R別配置格式),請調(diào)用callback(null)。 然后,Knockout將按順序查詢?nèi)魏纹渌缘难b載器,直到提供非空值。
loadViewModel(name, templateConfig, callback)
定義如下: 您想要使用自定義邏輯為給定的viewmodel配置(例如,與第三方模塊加載器或依賴注入系統(tǒng)集成)提供viewmodel工廠。
默認(rèn)組件加載器將在聲明它的任何注冊加載器上調(diào)用此函數(shù),將組件配置的viewModel部分轉(zhuǎn)換為createViewModel工廠函數(shù)。 然后,該函數(shù)被緩存,并為需要viewmodel的組件的每個新實(shí)例調(diào)用。
viewModelConfig值只是來自任何componentConfig對象的viewModel屬性。 例如,它可以是構(gòu)造函數(shù),或自定義格式,如{myViewModelType:'Something',options:{}}。
-
要提供一個createViewModel函數(shù),請調(diào)用回調(diào)(yourCreateViewModelFunction)。 ThecreateViewModel函數(shù)必須接受參數(shù)(params,componentInfo),并且必須在每次調(diào)用時(shí)同步返回一個新的viewmodel實(shí)例。
-
如果你不希望你的加載器為給定的參數(shù)提供一個createViewModel函數(shù)(例如,因?yàn)樗荒茏R別配置格式),call callback(null)。 然后,Knockout將按順序查詢?nèi)魏纹渌缘难b載器,直到提供非空值。
注冊自定義組件加載器
Knockout允許您同時(shí)使用多個組件加載器。 這是有用的,例如,您可以插入實(shí)現(xiàn)不同機(jī)制的加載器(例如,可以根據(jù)命名約定從后端服務(wù)器獲取模板;另一個可以使用依賴注入系統(tǒng)設(shè)置視圖模型)并使它們工作 一起。
因此,ko.components.loaders是一個包含當(dāng)前啟用的所有加載器的數(shù)組。 默認(rèn)情況下,此數(shù)組只包含一個項(xiàng)目:ko.components.defaultLoader。 要添加額外的裝載器,只需將它們插入到ko.components.loaders數(shù)組中。
控制優(yōu)先級
如果希望自定義加載器優(yōu)先于默認(rèn)加載器(因此它獲得第一次提供配置/值的機(jī)會),然后將其添加到數(shù)組的開頭。 如果您希望默認(rèn)加載器優(yōu)先(因此您的自定義加載器僅為未顯式注冊的組件調(diào)用),然后將其添加到數(shù)組的末尾。
例:
// Adds myLowPriorityLoader to the end of the loaders array. // It runs after other loaders, only if none of them returned a value. ko.components.loaders.push(myLowPriorityLoader); // Adds myHighPriorityLoader to the beginning of the loaders array. // It runs before other loaders, getting the first chance to return values. ko.components.loaders.unshift(myHighPriorityLoader)
如果需要,您可以從裝載器數(shù)組中刪除ko.components.defaultLoader。
調(diào)用順序
第一次Knockout需要構(gòu)造一個具有給定名稱的組件,它:
- 依次調(diào)用每個注冊的裝載器的getConfig函數(shù),直到第一個提供非nullcomponentConfig。
- 然后,使用此componentConfig對象,依次調(diào)用每個注冊的裝載程序的loadComponent函數(shù),直到第一個提供非空模板/ createViewModel對。
當(dāng)默認(rèn)加載器的loadComponent運(yùn)行時(shí),它同時(shí):
- 依次調(diào)用每個注冊的裝載器的loadTemplate函數(shù),直到第一個提供非空的DOM數(shù)組。
- 默認(rèn)加載器本身有一個loadTemplate函數(shù),它將一系列模板配置格式解析為DOM數(shù)組。
- 依次調(diào)用每個注冊的裝載器的loadViewModel函數(shù),直到第一個提供非空的createViewModel函數(shù)。
- 默認(rèn)加載器本身有一個loadViewModel函數(shù),它將一系列viewmodel配置格式解析為createViewModel函數(shù)。
自定義加載器可以插入此過程的任何部分,因此您可以控制提供配置,解釋配置,提供DOM節(jié)點(diǎn)或提供viewmodel工廠函數(shù)。通過將自定義加載器放入ko.components.loaders中的選定順序,您可以控制不同加載策略的優(yōu)先級順序。
示例1:設(shè)置命名約定的組件加載程序
要實(shí)現(xiàn)命名約定,您的自定義組件加載器只需要實(shí)現(xiàn)getConfig。 例如:
var namingConventionLoader = { getConfig: function(name, callback) { // 1. Viewmodels are classes corresponding to the component name. // e.g., my-component maps to MyApp.MyComponentViewModel // 2. Templates are in elements whose ID is the component name // plus '-template'. var viewModelConfig = MyApp[toPascalCase(name) + 'ViewModel'], templateConfig = { element: name + '-template' }; callback({ viewModel: viewModelConfig, template: templateConfig }); } }; function toPascalCase(dasherized) { return dasherized.replace(/(^|-)([a-z])/g, function (g, m1, m2) { return m2.toUpperCase(); }); } // Register it. Make it take priority over the default loader. ko.components.loaders.unshift(namingConventionLoader);
現(xiàn)在已注冊,您可以使用任何名稱引用組件(無需預(yù)先注冊它們),例如:
<div data-bind="component: 'my-component'"></div> <!-- Declare template --> <template id='my-component-template'>Hello World!</template> <script> // Declare viewmodel window.MyApp = window.MyApp || {}; MyApp.MyComponentViewModel = function(params) { // ... } </script>
示例2:使用自定義代碼加載外部文件的組件加載器
如果您的自定義加載器實(shí)現(xiàn)了loadTemplate和/或loadViewModel,那么您可以在加載過程中插入自定義代碼。 您還可以使用這些函數(shù)來解釋自定義配置格式。
例如,您可能需要啟用以下配置格式:
ko.components.register('my-component', {
template: { fromUrl: 'file.html', maxCacheAge: 1234 },
viewModel: { viaLoader: '/path/myvm.js' }
});...你可以使用自定義加載器。
以下自定義加載器將處理使用fromUrl值配置的加載模板:
var templateFromUrlLoader = { loadTemplate: function(name, templateConfig, callback) { if (templateConfig.fromUrl) { // Uses jQuery's ajax facility to load the markup from a file var fullUrl = '/templates/' + templateConfig.fromUrl + '?cacheAge=' + templateConfig.maxCacheAge; $.get(fullUrl, function(markupString) { // We need an array of DOM nodes, not a string. // We can use the default loader to convert to the // required format. ko.components.defaultLoader.loadTemplate(name, markupString, callback); }); } else { // Unrecognized config format. Let another loader handle it. callback(null); } } }; // Register it ko.components.loaders.unshift(templateFromUrlLoader);
...并且以下自定義加載器將負(fù)責(zé)加載使用簽名加載器值配置的視圖模型:
var viewModelCustomLoader = { loadViewModel: function(name, viewModelConfig, callback) { if (viewModelConfig.viaLoader) { // You could use arbitrary logic, e.g., a third-party // code loader, to asynchronously supply the constructor. // For this example, just use a hard-coded constructor function. var viewModelConstructor = function(params) { this.prop1 = 123; }; // We need a createViewModel function, not a plain constructor. // We can use the default loader to convert to the // required format. ko.components.defaultLoader.loadViewModel(name, viewModelConstructor, callback); } else { // Unrecognized config format. Let another loader handle it. callback(null); } } }; // Register it ko.components.loaders.unshift(viewModelCustomLoader);
如果你愿意,你可以將templateFromUrlLoader和viewModelCustomLoader結(jié)合到單個加載器中,方法是將loadTemplate和loadViewModel函數(shù)放在單個對象上。 然而,分離出這些問題是相當(dāng)不錯的,因?yàn)樗鼈兊膶?shí)現(xiàn)是相當(dāng)獨(dú)立的。
注意1:自定義組件加載器和自定義元素
如果使用組件加載器通過命名約定獲取組件,并且不使用ko.components.register注冊組件,那么這些組件不會自動用作自定義元素(因?yàn)槟€沒告訴Knockout他們存在)。
請參閱:第六章 組件(4) 自定義元素
注意2:與browserify集成
Browserify是一個流行的庫,用于以Node樣式的同步require語法引用JavaScript庫。它通常被認(rèn)為是替代AMD加載器,如require.js。然而,Browserify解決了一個相當(dāng)不同的問題:同步構(gòu)建時(shí)參考解析,而不是由AMD處理的異步運(yùn)行時(shí)參考解析。
因?yàn)锽rowserify是一個構(gòu)建時(shí)間工具,它不需要真正需要與KO組件的任何特殊集成,并且沒有必要實(shí)現(xiàn)任何類型的自定義組件加載器來使用它。您可以簡單地使用Browserify的require語句來抓取您的組件視圖模型的實(shí)例,然后顯式地注冊它們,例如:
// Note that the following *only* works with Browserify - not with require.js, // since it relies on require() returning synchronously. ko.components.register('my-browserify-component', { viewModel: require('myViewModel'), template: require('fs').readFileSync(__dirname + '/my-template.html', 'utf8') });
這使用brfs Browserify插件自動內(nèi)聯(lián).html文件,因此您需要使用類似于以下命令構(gòu)建腳本文件:
npm install brfs
browserify -t brfs main.js > bundle.js章節(jié)結(jié)語
至此,KnockoutJS的組件介紹完畢,未來章節(jié)將介紹一些Knockout的其他技術(shù)。感謝你的閱讀,希望我的這個KnockoutJS系列能夠幫助到你,如果覺著文章不錯,請點(diǎn)一波推薦,歡迎留言,轉(zhuǎn)載請注明出處,http://www.rzrgm.cn/smallprogram。謝謝

浙公網(wǎng)安備 33010602011771號