KnockoutJS - 组件



组件是组织大型应用程序UI代码、构建结构和提高代码可重用性的一种重要方式。

它继承自或嵌套于其他组件。对于加载和配置,它定义了自己的约定或逻辑。

它被打包以便在整个应用程序或项目中重用。代表应用程序的完整部分或小型控件/部件。它可以按需加载或预加载。

组件注册

组件可以使用ko.components.register() API 注册。它有助于在 KO 中加载和表示组件。注册需要组件名称和配置。配置指定如何确定viewModel和模板。

语法

组件可以按如下方式注册:

ko.components.register('component-name', {
   viewModel: {...},    //function code
   template: {....)	//function code
});
  • 组件名称可以是任何非空字符串。

  • viewModel是可选的,可以采用下一节列出的任何viewModel格式。

  • template是必需的,可以采用下一节列出的任何模板格式。

声明ViewModel

下表列出了可以用来注册组件的viewModel格式。

序号 viewModel格式及说明
1

构造函数

它为每个组件创建一个单独的viewModel对象。该对象或函数用于绑定到组件视图。

function SomeComponentViewModel(params) {
   this.someProperty = params.something;
}
ko.components.register('component name', {
   viewModel: SomeComponentViewModel,
   template: ...
});
2

共享对象实例

viewModel对象实例是共享的。传递instance属性以直接使用该对象。

var sharedViewModelInstance = { ... };

ko.components.register('component name', {
   viewModel: { instance: sharedViewModelInstance },
   template: ...
});
3

createViewModel

它调用一个充当工厂的函数,该函数可用作可以返回对象的视图模型。

ko.components.register('component name', {  
   viewModel: {  
      createViewModel: function (params, componentInfo) {  
         ...       //function code  
         ...
      }  
   },  
   template: ....  
});
4

AMD模块

这是一种用于定义模块的模块格式,其中模块和依赖项都是异步加载的。

ko.components.register('component name', {
   viewModel: { require: 'some/module/name' },
   template: ...
});

define(['knockout'], function(ko) {
   function MyViewModel() {
      // ...
   }

   return MyViewModel;
});

声明模板

下表列出了可以用来注册组件的模板格式。

序号 模板格式
1

元素ID

ko.components.register('component name', {
   template: { element: 'component-template' },
   viewModel: ...
});
2

元素实例

var elemInstance = document.getElementById('component-template');

ko.components.register('component name', {
   template: { element: elemInstance },
   viewModel: ...
});
3

标记字符串

ko.components.register('component name', {
   template: '<input data-bind = "value: yourName" />\
      <button data-bind = "click: addEmp">Add Emp </button>',
   viewModel: ...
});
4

DOM节点

var emp = [
   document.getElementById('node 1'),
   document.getElementById('node 2'),
];

ko.components.register('component name', {
   template: emp,
   viewModel: ...
});
5

文档片段

ko.components.register('component name', {
   template: someDocumentFragmentInstance,
   viewModel: ...
});
6

AMD模块

ko.components.register('component name', {
   template: { require: 'some/template' },
   viewModel: ...
});

作为单个AMD模块注册的组件

AMD模块本身可以注册组件,无需使用viewModel/template对。

ko.components.register('component name',{ require: 'some/module'});

组件绑定

有两种组件绑定方式。

  • 完整语法 - 它将参数和对象传递给组件。它可以使用以下属性传递。

    • name - 添加组件名称。

    • params - 它可以在组件上以对象形式传递多个参数。

<div data-bind='component: {
   name: "tutorials point",
   params: { mode: "detailed-list", items: productsList }
}'>
</div>
  • 简写语法 - 它将字符串作为组件名称传递,并且不包含参数。

<div data-bind = 'component: "component name"'></div>
  • 仅模板组件 - 组件只能定义模板,而不指定viewModel。

ko.components.register('component name', {
   template:'<input data-bind = "value: someName" />,
});
  • 无需容器元素即可使用组件 - 组件无需使用额外的容器元素即可使用。这可以通过使用无容器流控制来完成,这类似于注释标签。

<!--ko.component: ""-->
<!--/ko-->

自定义元素

自定义元素是渲染组件的一种方式。在这里,您可以直接编写自描述的标记元素名称,而不是定义占位符,组件通过它进行绑定。

<products-list params = "name: userName, type: userType"></products-list>

传递参数

params属性用于将参数传递给组件viewModel。它类似于data-bind属性。params属性的内容被解释为JavaScript对象文字(就像data-bind属性一样),因此您可以传递任何类型的任意值。它可以按以下方式传递参数:

  • 父组件和子组件之间的通信 - 组件本身不会被实例化,因此viewmodel属性是从组件外部引用的,因此将被子组件viewmodel接收。例如,您可以在下面的语法中看到ModelValue是父viewmodel,它被子viewmodel构造函数ModelProperty接收。

  • 传递可观察表达式 - params参数中有三个值。

    • simpleExpression - 它是一个数值。它不涉及任何可观察对象。

    • simpleObservable - 它是定义在父viewModel上的实例。父viewModel将自动获取子viewModel对可观察对象所做的更改。

    • observableExpression - 表达式在表达式本身被求值时读取可观察对象。当可观察对象的值发生变化时,表达式的结果也会随着时间而变化。

我们可以按如下方式传递参数:

<some-component
   params = 'simpleExpression: 1 + 1,
      simpleObservable: myObservable,
      observableExpression: myObservable() + 1'>
</some-component>

我们可以按如下方式在viewModel中传递参数:

<some-component
   params = 'objectValue:{a: 3, b: 2},
      dateValue: new date(),
      stringValue: "Hi",
      numericValue:123,
      boolValue: true/false,
      ModelProperty: ModelValue'>
</some-component>

将标记传递到组件中

接收到的标记用于创建组件,并被选择作为输出的一部分。以下节点作为组件模板中的输出的一部分传递。

template: { nodes: $componentTemplateNodes }

控制自定义元素标签名称

使用ko.components.register在组件中注册的名称,与自定义元素标签名称相同。我们可以通过覆盖它来控制使用getComponentNameForNode

ko.components.getComponentNameForNode = function(node) {
   ...
   ...   //function code
   ...
}

注册自定义元素

如果使用默认组件加载器,则可以立即使用自定义元素,因此组件是使用ko.components.register注册的。如果我们没有使用ko.components.register并实现自定义组件加载器,则可以使用任何选择的元素名称来使用自定义元素。当您使用ko.components.register时,无需指定配置,因为自定义组件加载器不再使用它。

ko.components.register('custom-element', { ......... });

示例

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Components</title>
      <script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
      <script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
   </head>
   
   <body>
      <!--params attribute is used to pass the parameter to component viewModel.-->
      <click params = "a: a, b: b"></click>

      <!--template is used for a component by specifying its ID -->
      <template id = "click-l">
         <div data-bind = "text: a"></div>

         <!--Use data-bind attribute to bind click:function() to ViewModel. -->
         <button data-bind = "click:function(){callback(1)}">Increase</button>
         <button data-bind = "click:function(){callback(-1)}">Decrease</button>
      </template>

      <script>
         //Here components are registered
         ko.components.register('click', {
            
            viewModel: function(params) {
               self = this;
               this.a = params.a;
               this.b = params.b;

               this.callback = function(num) {
                  self.b(parseInt(num));
                  self.a( self.a() + parseInt(num) );
               };
            },
            template: { element: 'click-l' }
         });

         //keeps an eye on variable for any modification in data
         function viewModel() {
            this.a = ko.observable(2);
            this.b = ko.observable(0);
         }

         ko.applyBindings(new viewModel() );
      </script>
      
   </body>
</html>

输出

让我们执行以下步骤来查看上述代码是如何工作的:

  • 将上述代码保存在component_register.htm文件中。

  • 在浏览器中打开此HTML文件。

组件加载器

组件加载器用于为给定的组件名称异步传递template/viewModel对。

默认组件加载器

默认组件加载器取决于显式注册的配置。每个组件在使用前都会被注册。

ko.components.defaultLoader

组件加载器实用函数

默认组件加载器可以使用以下函数进行读写。

序号 实用函数及说明
1

ko.components.register(name, configuration)

注册组件。

2

ko.components.isRegistered(name)

如果特定组件名称已注册,则返回true,否则返回false。

3

ko.components.unregister(name)

从注册表中删除组件名称。

4

ko.components.get(name, callback)

此函数依次转到每个注册的加载器,以查找谁首先传递了组件名称的viewModel/template定义。然后,它通过调用callback来返回viewModel/template声明。如果注册的加载器找不到关于组件的任何信息,则它将调用callback(null)

5

ko.components.clearCachedDefinition(name)

当我们想要清除给定的组件缓存条目时,可以调用此函数。如果下次需要该组件,加载器将再次被咨询。

实现自定义组件加载器

自定义组件加载器可以按以下方式实现:

  • getConfig(name, callback) - 根据名称,我们可以以编程方式传递配置。我们可以调用callback(componentConfig)来传递配置,其中对象componentConfig可以被loadComponent或任何其他加载器使用。

  • loadComponent(name, componentConfig, callback) - 此函数根据配置方式解析config的viewModel和template部分。我们可以调用callback(result)来传递viewmodel/template对,其中对象result由以下属性定义。

    • template - 必需。返回DOM节点数组。

    • createViewModel(params, componentInfo) - 可选。根据viewModel属性的配置方式返回viewModel对象。

  • loadTemplate(name, templateConfig, callback) - 使用自定义逻辑在模板中传递DOM节点。对象templateConfig是来自对象componentConfig的模板的属性。callback(domNodeArray)被调用以传递DOM节点数组。

  • loadViewModel(name, templateConfig, callback) - 使用自定义逻辑在viewModel配置中传递viewModel工厂。

广告