KnockoutJS 3.X API 第六章 組件(5) 高級應用組件加載器

不管什麼時候使用組件綁定或自定義元素注入組件,Knockout都將使用一個或多個組件裝載器獲取該組件的模板和視圖模型。 組件加載器的任務是異步提供任何給定組件名稱的模板/視圖模型對。html

本節目錄

  • 默認組件加載器
  • 組件加載器實用函數
  • 實現自定義組件加載器
    • 能夠實現的功能
      • getConfig(name, callback)
      • loadComponent(name, componentConfig, callback)
      • loadTemplate(name, templateConfig, callback)
      • loadViewModel(name, templateConfig, callback)
    • 註冊自定義組件加載器
    • 控制優先級
    • 調用順序
  • 示例1:設置命名約定的組件加載程序
  • 示例2:使用自定義代碼加載外部文件的組件加載器
  • 注意1:自定義組件加載器和自定義元素
  • 注意2:與browserify集成

默認組件加載器

內置的默認組件加載器ko.components.defaultLoader基於組件定義的中心「註冊表」。 它依賴於您明確註冊每一個組件的配置,而後才能使用該組件。node

可參考第六章第一節。ajax

組件加載器實用函數

如下函數讀取和寫入默認組件加載器的註冊表: npm

  • ko.components.register(name, configuration)
    • 註冊組件
  • ko.components.isRegistered(name)
    • 若是具備指定名稱的組件已註冊,則返回true;不然爲假。
  • ko.components.unregister(name)
    • 從註冊表中刪除指定的組件。或者若是沒有註冊這樣的組件,什麼都不作。

如下函數在註冊的組件加載器的完整列表中工做(不只是默認加載器):
編程

  • ko.components.get(name, callback)
    • 依次查詢每一個註冊的加載器(默認狀況下,只是默認加載器),找到第一個爲命名的組件提供viewmodel /模板定義,而後調用回調來返回比viewmodel / template聲明。若是沒有註冊的裝載器知道這個組件,則調用回調(null)。
  • ko.components.clearCachedDefinition(name)
    • 一般,Knockout對每一個組件名稱查詢加載器一次,而後緩存生成的定義。這確保了能夠很是快速地實例化大量組件。若是要清除給定組件的緩存條目,請調用此方法,而後在下次須要該組件時再次查詢加載程序。

此外,因爲ko.components.defaultLoader是組件加載器,它實現如下標準組件加載器函數。您能夠直接調用這些方法,例如,做爲自定義加載器實施的一部分: 後端

  • 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)

實現自定義組件加載器

若是要使用命名約定而不是顯式註冊來加載組件,則可能須要實現自定義組件加載器。 或者,若是您想使用第三方「加載器」庫從外部位置獲取組件視圖模型或模板。數組

能夠實現的功能

自定義組件加載器只是一個對象,其屬性是如下函數的任意組合:緩存

getConfig(name, callback)

定義以下: 您但願基於名稱以編程方式提供配置,例如,實現命名約定。 服務器

若是聲明,Knockout將調用此函數爲每一個正在被實例化的組件獲取一個配置對象。 dom

  • 要提供配置,請調用回調(componentConfig),其中componentConfig是加載器或任何其餘加載器上的loadComponent函數能夠理解的任何對象。 默認加載器只提供使用ko.components.register註冊的任何對象。
  • 例如,一個componentConfig像{template:'someElementId',viewModel:{require:'myModule'}}能夠被默認加載器理解和實例化。
  • 您不限於以任何標準格式提供配置對象。 只要loadComponent函數理解它們,就能夠提供任意對象。
  • 若是你不但願你的加載器提供一個命名組件的配置,那麼callcallback(null)。 而後,Knockout將按順序查詢任何其餘註冊的裝載器,直到提供非空值。
loadComponent(name, componentConfig, callback)

定義以下: 您想要控制組件配置的解釋方式,例如,若是您不想使用標準的viewModel /模板對格式。

若是聲明,Knockout將調用此函數將componentConfig對象轉換爲viewmodel /模板對。

  • 要提供一個viewmodel /模板對,請調用callback(result),其中result是具備如下屬性的對象:

    • template - 必需。 DOM節點數組
    • createViewModel(params, componentInfo) - 可選。 稍後將調用的函數覺得此組件的每一個實例提供一個viewmodel對象
  • 若是你不但願你的加載器爲給定的參數提供一個viewmodel /模板對,那麼callcallback(null)。 而後,Knockout將按順序查詢任何其餘註冊的裝載器,直到提供非空值。

loadTemplate(name, templateConfig, callback)

定義以下: 您想要使用自定義邏輯爲給定模板配置提供DOM節點(例如,使用ajax請求經過URL提取模板)。

默認組件加載器將在聲明它的任何註冊加載器上調用此函數,將組件配置的template部分轉換爲DOM節點數組。 而後,爲組件的每一個實例緩存和克隆節點。

templateConfig值只是來自任何componentConfig對象的template屬性。 例如,它可能包含「some markup」或{element:「someId」}或自定義格式,如{loadFromUrl:「someUrl.html」}。

  • 要提供DOM節點的數組,請調用回調(domNodeArray)。

  • 若是您不但願您的加載程序爲給定的參數提供模板(例如,由於它不能識別配置格式),請調用callback(null)。 而後,Knockout將按順序查詢任何其餘註冊的裝載器,直到提供非空值。

loadViewModel(name, templateConfig, callback)

定義以下: 您想要使用自定義邏輯爲給定的viewmodel配置(例如,與第三方模塊加載器或依賴注入系統集成)提供viewmodel工廠。

默認組件加載器將在聲明它的任何註冊加載器上調用此函數,將組件配置的viewModel部分轉換爲createViewModel工廠函數。 而後,該函數被緩存,併爲須要viewmodel的組件的每一個新實例調用。

viewModelConfig值只是來自任何componentConfig對象的viewModel屬性。 例如,它能夠是構造函數,或自定義格式,如{myViewModelType:'Something',options:{}}。

  • 要提供一個createViewModel函數,請調用回調(yourCreateViewModelFunction)。 ThecreateViewModel函數必須接受參數(params,componentInfo),而且必須在每次調用時同步返回一個新的viewmodel實例。

  • 若是你不但願你的加載器爲給定的參數提供一個createViewModel函數(例如,由於它不能識別配置格式),call callback(null)。 而後,Knockout將按順序查詢任何其餘註冊的裝載器,直到提供非空值。

 

註冊自定義組件加載器

Knockout容許您同時使用多個組件加載器。 這是有用的,例如,您能夠插入實現不一樣機制的加載器(例如,能夠根據命名約定從後端服務器獲取模板;另外一個可使用依賴注入系統設置視圖模型)並使它們工做 一塊兒。

所以,ko.components.loaders是一個包含當前啓用的全部加載器的數組。 默認狀況下,此數組只包含一個項目:ko.components.defaultLoader。 要添加額外的裝載器,只需將它們插入到ko.components.loaders數組中。

控制優先級

若是但願自定義加載器優先於默認加載器(所以它得到第一次提供配置/值的機會),而後將其添加到數組的開頭。 若是您但願默認加載器優先(所以您的自定義加載器僅爲未顯式註冊的組件調用),而後將其添加到數組的末尾。

例:

// 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)

若是須要,您能夠從裝載器數組中刪除ko.components.defaultLoader。

調用順序

第一次Knockout須要構造一個具備給定名稱的組件,它:

  • 依次調用每一個註冊的裝載器的getConfig函數,直到第一個提供非nullcomponentConfig。
  • 而後,使用此componentConfig對象,依次調用每一個註冊的裝載程序的loadComponent函數,直到第一個提供非空模板/ createViewModel對。

當默認加載器的loadComponent運行時,它同時:

  • 依次調用每一個註冊的裝載器的loadTemplate函數,直到第一個提供非空的DOM數組。
    • 默認加載器自己有一個loadTemplate函數,它將一系列模板配置格式解析爲DOM數組。
  • 依次調用每一個註冊的裝載器的loadViewModel函數,直到第一個提供非空的createViewModel函數。
    • 默認加載器自己有一個loadViewModel函數,它將一系列viewmodel配置格式解析爲createViewModel函數。

自定義加載器能夠插入此過程的任何部分,所以您能夠控制提供配置,解釋配置,提供DOM節點或提供viewmodel工廠函數。經過將自定義加載器放入ko.components.loaders中的選定順序,您能夠控制不一樣加載策略的優先級順序。

示例1:設置命名約定的組件加載程序

要實現命名約定,您的自定義組件加載器只須要實現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);

如今已註冊,您可使用任何名稱引用組件(無需預先註冊它們),例如:

<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:使用自定義代碼加載外部文件的組件加載器

若是您的自定義加載器實現了loadTemplate和/或loadViewModel,那麼您能夠在加載過程當中插入自定義代碼。 您還可使用這些函數來解釋自定義配置格式。

例如,您可能須要啓用如下配置格式:

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);

...而且如下自定義加載器將負責加載使用簽名加載器值配置的視圖模型:

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結合到單個加載器中,方法是將loadTemplate和loadViewModel函數放在單個對象上。 然而,分離出這些問題是至關不錯的,由於它們的實現是至關獨立的。

注意1:自定義組件加載器和自定義元素

若是使用組件加載器經過命名約定獲取組件,而且不使用ko.components.register註冊組件,那麼這些組件不會自動用做自定義元素(由於您還沒告訴Knockout他們存在)。

請參閱:第六章 組件(4) 自定義元素

注意2:與browserify集成

Browserify是一個流行的庫,用於以Node樣式的同步require語法引用JavaScript庫。它一般被認爲是替代AMD加載器,如require.js。然而,Browserify解決了一個至關不一樣的問題:同步構建時參考解析,而不是由AMD處理的異步運行時參考解析。

由於Browserify是一個構建時間工具,它不須要真正須要與KO組件的任何特殊集成,而且沒有必要實現任何類型的自定義組件加載器來使用它。您能夠簡單地使用Browserify的require語句來抓取您的組件視圖模型的實例,而後顯式地註冊它們,例如:

// 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插件自動內聯.html文件,所以您須要使用相似於如下命令構建腳本文件:

npm install brfs
browserify -t brfs main.js > bundle.js

章節結語

至此,KnockoutJS的組件介紹完畢,將來章節將介紹一些Knockout的其餘技術。感謝你的閱讀,但願個人這個KnockoutJS系列可以幫助到你,若是覺着文章不錯,請點一波推薦,歡迎留言,轉載請註明出處,http://www.cnblogs.com/smallprogram。謝謝

相關文章
相關標籤/搜索