KnockoutJS 3.X API 第六章 组件(2) 组件注册
要使Knockout能够加载和实例化组件,必须使用ko.components.register注册它们,从而提供如此处所述的配置。
注意:作为替代,可以实现一个自定义组件加载器(自定义加载器下一节介绍),通过自己的约定获取组件,而不是显式配置。
本节目录
- 将组件注册为viewmodel/template
- 指定视图模型
- 构造函数
- 共享对象实例
- 一个createViewModel工厂函数
- 一个AMD模块,其值描述一个viewmodel
- 指定模板
- 现有元素ID
- 现有元素实例
- 一串标记
- DOM节点数组
- 文档片段
- 其值描述模板的AMD模块
- 指定其他组件选项
- 控制同步/异步加载
- 指定视图模型
- Knockout如何通过AMD加载组件
- AMD模块只在需要时加载
- 将组件注册为单个AMD模块
- 推荐的AMD模块模式
将组件注册为viewmodel/template
您可以用如下方式注册组件:
ko.components.register('some-component-name', {
viewModel: <see below>,
template: <see below>
});
- 组件名称可以是任何非空字符串。 建议(但不是强制性的)使用小写分隔符的字符串(例如your-component-name),以便组件名称可用作自定义元素(例如<your-component-name>)。
- viewModel是可选的,并且可以采用下面描述的任何viewModel格式。
- template是必填的,并且可以采用下面描述的任何模板格式。
如果没有给出viewmodel,组件将被视为一个简单的HTML块,它将绑定到传递给组件的任何参数。
指定视图模型
视图模型可以通过以下任何形式指定:
构造函数:
function SomeComponentViewModel(params) {
// 'params' is an object whose key/value pairs are the parameters
// passed from the component binding or custom element.
this.someProperty = params.something;
}
SomeComponentViewModel.prototype.doSomething = function() { ... };
ko.components.register('my-component', {
viewModel: SomeComponentViewModel,
template: ...
});
Knockout将为组件的每个实例调用一次构造函数,为每个实例生成一个单独的viewmodel对象。 生成的对象或其原型链上的属性(例如,上面示例中的someProperty和doSomething)可用于在组件视图中绑定。
共享对象实例:
如果你想要你的组件的所有实例共享同一个viewmodel对象实例(这通常是不可取的):
var sharedViewModelInstance = { ... };
ko.components.register('my-component', {
viewModel: { instance: sharedViewModelInstance },
template: ...
});
注意,有必要指定viewModel:{instance:object},而不仅仅是viewModel:object。 这与下面的其他情况不同。
一个createViewModel工厂函数:
如果要在关联元素绑定到viewmodel之前运行任何设置逻辑,或使用任意逻辑来决定要实例化哪个viewmodel类:
ko.components.register('my-component', {
viewModel: {
createViewModel: function(params, componentInfo) {
// - 'params' is an object whose key/value pairs are the parameters
// passed from the component binding or custom element
// - 'componentInfo.element' is the element the component is being
// injected into. When createViewModel is called, the template has
// already been injected into this element, but isn't yet bound.
// - 'componentInfo.templateNodes' is an array containing any DOM
// nodes that have been supplied to the component. See below.
// Return the desired view model instance, e.g.:
return new MyViewModel(params);
}
},
template: ...
});
注意,通常,最好仅通过自定义绑定执行直接DOM操作,而不是从createViewModel内部对componentInfo.element执行操作。 这导致更多的模块化,可重用的代码。
如果要构建一个接受任意标记以影响其输出的组件(例如,将提供的标记插入到其自身中的网格,列表,对话框或选项卡集),则componentInfo.templateNodes数组很有用。 有关完整的示例,请参阅将标记传递到组件(将在以后的章节介绍)。
一个AMD模块,其值描述一个viewmodel
如果您的页面中已经有一个AMD加载器(例如require.js),那么您可以使用它来获取一个viewmodel。 有关如何工作的更多细节,看看Knockout如何通过AMD加载组件下面。 例:
ko.components.register('my-component', {
viewModel: { require: 'some/module/name' },
template: ...
});
返回的AMD模块对象可以是允许用于viewmodels的任何形式。 因此,它可以是一个构造函数,例如:
// AMD module whose value is a component viewmodel constructor
define(['knockout'], function(ko) {
function MyViewModel() {
// ...
} return MyViewModel;
});
..或共享对象实例,例如:
// AMD module whose value is a shared component viewmodel instance
define(['knockout'], function(ko) {
function MyViewModel() {
// ...
} return { instance: new MyViewModel() };
});
..或createViewModel函数,例如:
// AMD module whose value is a 'createViewModel' function
define(['knockout'], function(ko) {
function myViewModelFactory(params, componentInfo) {
// return something
} return { createViewModel: myViewModelFactory };
});
...甚至,虽然你不太可能想要这样做,一个参考不同的AMD模块,例如:
// AMD module whose value is a reference to a different AMD module,
// which in turn can be in any of these formats
define(['knockout'], function(ko) {
return { module: 'some/other/module' };
});
指定模板
模板可以以下列任何形式指定。 最常用的是现有的元素ID和AMD模块。
现有的元素ID
例如,以下元素:
<template id='my-component-template'>
<h1 data-bind='text: title'></h1>
<button data-bind='click: doSomething'>Click me right now</button>
</template>
..可以通过指定其ID用作组件的模板:
ko.components.register('my-component', {
template: { element: 'my-component-template' },
viewModel: ...
});
注意,只有指定元素内的节点将被克隆到组件的每个实例中。 容器元素(在此示例中为<template>元素)不会被视为组件模板的一部分。
您不仅限于使用<template>元素,但这些方法(在支持它们的浏览器上)很方便,因为它们不会自己呈现。 任何其他元素类型也工作。
现有元素实例
如果你有一个对代码中的DOM元素的引用,你可以使用它作为模板标记的容器:
var elemInstance = document.getElementById('my-component-template');
ko.components.register('my-component', {
template: { element: elemInstance },
viewModel: ...
});
同样,只有指定元素中的节点将被克隆用作组件模板。
一串标记
ko.components.register('my-component', {
template: '<h1 data-bind="text: title"></h1>\
<button data-bind="click: doSomething">Clickety</button>',
viewModel: ...
});
当你从编程方式(例如,AMD )获取标记,或者作为一个构建系统输出打包组件进行分发时,这是非常有用的,因为手动编辑HTML作为JavaScript字符串文字并不是很方便。
DOM节点数组
如果您以编程方式构建配置,并且具有DOM节点数组,则可以将它们用作组件模板:
var myNodes = [
document.getElementById('first-node'),
document.getElementById('second-node'),
document.getElementById('third-node')
]; ko.components.register('my-component', {
template: myNodes,
viewModel: ...
});
在这种情况下,所有指定的节点(及其后代)将被克隆并连接到实例化的组件的每个副本。
文档片段
如果您以编程方式构建配置,并且具有Document Fragment对象,则可以将其用作组件模板:
ko.components.register('my-component', {
template: someDocumentFragmentInstance,
viewModel: ...
});
由于文档片段可以具有多个顶级节点,因此整个文档片段(不仅仅是顶级节点的后代)被视为组件模板。
其值描述模板的AMD模块
如果您的页面中已经有一个AMD加载器(例如require.js),那么您可以使用它来获取模板。 有关如何工作的更多细节,看看Knockout如何通过AMD加载组件下面。 例:
ko.components.register('my-component', {
template: { require: 'some/template' },
viewModel: ...
});
返回的AMD模块对象可以是允许用于viewmodels的任何形式。 因此,它可以是一串标记,例如。 使用requirejs文本插件获取:
ko.components.register('my-component', {
template: { require: 'text!path/my-html-file.html' },
viewModel: ...
});
...或这里描述的任何其他形式,虽然它是不常用的,其他人在通过AMD抓取模板时有用。
指定其他组件选项
除了(或代替)template和viewModel,您的组件配置对象可以具有任意其他属性。 此配置对象可用于您可能正在使用的任何自定义组件加载器。
控制同步/异步加载
如果您的组件配置具有布尔同步属性,Knockout使用此属性来确定是否允许同步加载和注入组件。 默认值为false(即,强制为异步)。 例如,
ko.components.register('my-component', {
viewModel: { ... anything ... },
template: { ... anything ... },
synchronous: true // Injects synchronously if possible, otherwise still async
});
为什么组件加载通常被强制为异步?
通常,Knockout确保组件加载和组件注入总是异步完成,因为有时它不得不异步(例如,因为它涉及到服务器的请求)。 即使特定组件实例可以被同步注入(例如,因为组件定义已经被加载),它也会这样做。 这种始终异步的策略是一致性的问题,是一种从其他现代异步JavaScript技术(如AMD)继承而来的公认惯例。 约定是一个安全的默认 - 它减轻了潜在的错误,在这种情况下,开发人员可为什么会启用同步加载?能不会考虑通常异步过程有时完成同步或反之亦然的可能性。
为什么会启用同步加载?
如果要更改特定组件的策略,可以在该组件的配置上指定synchronous:true。 然后,它可能在第一次使用时异步加载,随后在所有后续使用上同步加载。 如果这样做,那么您需要在任何等待加载组件的代码中考虑这种可更改的行为。 但是,如果您的组件始终可以同步加载和初始化,则启用此选项将确保一致的同步行为。 如果您在foreach绑定中使用组件并想要使用afterAdd或afterRender选项执行后处理,这可能很重要。
在Knockout 3.4.0之前,您可能需要使用同步加载来防止多个DOM回流同时包含多个组件(例如使用foreach绑定)。 使用Knockout 3.4.0,组件使用Knockout的微任务来确保异步,因此通常会执行和同步加载一样好。
Knockout如何通过AMD加载组件
当您通过require声明加载视图模型或模板时,例如,
ko.components.register('my-component', {
viewModel: { require: 'some/module/name' },
template: { require: 'text!some-template.html' }
});
...所有Knockout是调用require(['some / module / name'],回调)和require(['text!some-template.html'],回调),并使用异步返回的对象作为viewmodel和模板 定义。 所以,
- 这不需要严格依赖require.js或任何其他特定的模块加载器。 任何提供AMD风格的require API的模块加载器都可以。 如果要与API的模块加载器集成,可以实现自定义组件加载器。
- Knockout不以任何方式解释模块名称 - 它只是将它传递给require()。 所以当然Knockout不知道或关心你的模块文件从哪里加载。 这取决于你的AMD加载器和你如何配置它。
- Knockout不知道或关心您的AMD模块是否是匿名的。 通常,我们发现最方便的组件被定义为匿名模块,但这个问题完全独立于KO。
AMD模块只在需要时加载
Knockout不调用require([moduleName],...),直到组件被实例化。 这是组件如何按需加载,而不是前面。
例如,如果你的组件里面有一个if绑定(或另一个控制流绑定)的其他元素,那么它不会导致AMD模块加载,直到if条件为真。 当然,如果AMD模块已经加载(例如,在预加载的bundle中),则require调用不会触发任何额外的HTTP请求,因此您可以控制预加载的内容和按需加载的内容。
将组件注册为单个AMD模块
为了更好的封装,您可以将组件封装到一个自我描述的AMD模块中。 然后你可以简单地引用一个组件:
ko.components.register('my-component', { require: 'some/module' });
注意,没有指定viewmodel / template对。 AMD模块本身可以使用上面列出的任何定义格式提供viewmodel /template对。 例如,文件some / module.js可以声明为:
// AMD module 'some/module.js' encapsulating the configuration for a component
define(['knockout'], function(ko) {
function MyComponentViewModel(params) {
this.personName = ko.observable(params.name);
} return {
viewModel: MyComponentViewModel,
template: 'The name is <strong data-bind="text: personName"></strong>'
};
});
推荐的AMD模块模式
在实践中最有用的是创建具有内联viewmodel类的AMD模块,并显式地将AMD依赖关系放在外部模板文件上。
例如,如果以下是在路径/ my-component.js的文件中,
// Recommended AMD module pattern for a Knockout component that:
// - Can be referenced with just a single 'require' declaration
// - Can be included in a bundle using the r.js optimizer
define(['knockout', 'text!./my-component.html'], function(ko, htmlString) {
function MyComponentViewModel(params) {
// Set up properties, etc.
} // Use prototype to declare any public methods
MyComponentViewModel.prototype.doSomething = function() { ... }; // Return component definition
return { viewModel: MyComponentViewModel, template: htmlString };
});
...并且模板标记位于文件路径/ my-component.html中,则您具有以下好处:
- 应用程序可以简单地引用它,即ko.components.register('my-component',{require:'path / my-component'});
- 你只需要两个文件的组件 - 一个viewmodel(path / my-component.js)和一个模板(path / my-component.html) - 这是一个非常自然的安排在开发过程中。
- 因为在define调用中明确声明了对模板的依赖,所以这自动与r.js优化器或类似的捆绑工具配合使用。 因此,在构建步骤期间,整个组件 - 视图模型和模板 - 可以轻易地包含在包文件中。
KnockoutJS 3.X API 第六章 组件(2) 组件注册的更多相关文章
- KnockoutJS 3.X API 第六章 组件(5) 高级应用组件加载器
无论何时使用组件绑定或自定义元素注入组件,Knockout都将使用一个或多个组件装载器获取该组件的模板和视图模型. 组件加载器的任务是异步提供任何给定组件名称的模板/视图模型对. 本节目录 默认组件加 ...
- KnockoutJS 3.X API 第六章 组件(3) 组件绑定
组件绑定将指定的组件注入到元素中,并且可选地将参数传递给它. 本节目录 一个例子 API 组件生命周期 备注1:仅限模板组件 备注2:使用没有容器元素的组件 备注3:将标记传递给组件 处置和内存管理 ...
- KnockoutJS 3.X API 第六章 组件(4) 自定义元素
自定义元素提供了一种将组件注入视图的方便方法. 本节目录 介绍 例子 传递参数 父组件和子组件之间的通信 传递监控属性的表达式 将标记传递到组件中 控制自定义元素标记名称 注册自定义元素 备注1:将自 ...
- KnockoutJS 3.X API 第六章 组件(1) 组件和自定义元素 - 概述
Components (组件)是一个强大的,干净的方式组织您的UI代码,可重复使用的块. : -可以表示单独的控件/窗口小部件或应用程序的整个部分 -包含自己的视图,通常(但可选)自己的视图模型 -可 ...
- 第六章、forms组件
目录 第六章.forms组件 一.注册功能手写 二.forms组件完整写法 基本使用 三.forms组件前端渲染标签组件 三.forms组件其他知识点 在python console测试 校验数据 f ...
- KnockoutJS 3.X API 第七章 其他技术(8) 异步错误处理
注意:本文档适用于Knockout 3.4.0及更高版本. ko.onError Knockout包装内部异步调用,并在抛出原始错误之前查找可选的ko.onError回调以执行(如果遇到异常). 这使 ...
- KnockoutJS 3.X API 第四章 数据绑定(5) 控制流component绑定
本节目录: 一个例子 API 备注1:仅模板式的component 备注2:component虚拟绑定 备注3:传递标记到component绑定 内存管理 一个例子 First instance, w ...
- KnockoutJS 3.X API 第七章 其他技术(7) 微任务
注意:本文档适用于Knockout 3.4.0及更高版本. Knockout的微任务队列 Knockout的微任务队列支持调度任务尽可能快地运行,同时仍然是异步的,努力安排它们在发生I / O,回流或 ...
- KnockoutJS 3.X API 第七章 其他技术(4) 速率限制
注意:这个速率限制API是在Knockout 3.1.0中添加的. 通常,更改的observable立即通知其订户,以便依赖于observable的任何计算的observable或绑定都会同步更新. ...
随机推荐
- 向上滚动或者向下滚动分页异步加载数据(Ajax + lazyload)[上拉加载组件]
/**** desc : 分页异步获取列表数据,页面向上滚动时候加载前面页码,向下滚动时加载后面页码 ajaxdata_url ajax异步的URL 如data.php page_val_name a ...
- 《转》Unity3D研究院编辑器之创建Lua脚本模板
Unity里能创建 c#脚本模板,但是如果我想创建Lua脚本模板怎么办呢?拓展一下编辑器吧. 设置一下Lua脚本的模板地址 : Assets/Editor/Lua/Template/lua.lua ...
- 控制Storyboard播放zz
<Grid Width="300" Height="460"> <Grid.RowDefinitions> <RowDefinit ...
- java工厂模式
(1)概念大白话:java工厂模式就是客户端(main函数)要创建对象觉得麻烦就让另外一个叫工厂的类帮它创建,然后自己每次要创建对象就叫工厂帮它弄,举个例子,在没有工厂这个"手下" ...
- 【填坑】bzoj3224 splay裸题
人生第一道splay不出所料是一道裸题,一道水题,一道2k代码都不到的题 #include <cstdio> ,n,p,q; ],c[][],size[],sp[]; void rot(i ...
- web应用和虚拟目录映射
Tip:WEB应用程序 WEB应用程序指供浏览器访问的程序,通常简称为web应用. 一个web应用由多个静态web资源和动态web资源组成,如: HTML.css.js文件 JSP文件.java程序. ...
- javascript中三种典型情况下this的含义
this本意:基于函数的执行环境绑定. 1)一般函数内部,返回的是window(作用域链中的第二层全局作用域) function test() { return this; } alert(test( ...
- 配置nginx的图片服务器
user nginx; worker_processes 8; error_log /usr/local/webserver/nginx/logs/nginx_error.log crit; pid ...
- PHP图形操作之生成图像验证码
简单的验证码其实就是在图片中输出了几个字符,通过imagestring函数就能实现. 但是在处理上,为了使验证码更加的安全,防止其他程序自动识别,因此常常需要对验证码进行一些干扰处理,通常会采用绘制一 ...
- 通读SDWebImage③--gif和webP的支持、不同格式图片的处理、方向处理
本文目录 NSData+ImageContentType: 根据NSData获取MIME UIImage+GIF UIImage+WebP UIImage+MultiFormat:根据NSData相应 ...