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。谢谢