KnockoutJS 3.X API 第六章 组件(5) 高级应用组件加载器

无论何时使用组件绑定或自定义元素注入组件,Knockout都将使用一个或多个组件装载器获取该组件的模板和视图模型。 组件加载器的任务是异步提供任何给定组件名称的模板/视图模型对。

本节目录

  • 默认组件加载器
  • 组件加载器实用函数
  • 实现自定义组件加载器
    • 可以实现的功能
      • getConfig(name, callback)
      • loadComponent(name, componentConfig, callback)
      • loadTemplate(name, templateConfig, callback)
      • loadViewModel(name, templateConfig, callback)
    • 注册自定义组件加载器
    • 控制优先级
    • 调用顺序
  • 示例1:设置命名约定的组件加载程序
  • 示例2:使用自定义代码加载外部文件的组件加载器
  • 注意1:自定义组件加载器和自定义元素
  • 注意2:与browserify集成

默认组件加载器

内置的默认组件加载器ko.components.defaultLoader基于组件定义的中心“注册表”。 它依赖于您明确注册每个组件的配置,然后才能使用该组件。

可参考第六章第一节。

组件加载器实用函数

以下函数读取和写入默认组件加载器的注册表:

  • 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将调用此函数为每个正在被实例化的组件获取一个配置对象。

  • 要提供配置,请调用回调(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。谢谢

上一篇:【转】手把手教你 Mockito 的使用


下一篇:TCP模型及其重点协议总结