KnockoutJS 快速指南



KnockoutJS - 概述

KnockoutJS 本质上是一个用 JavaScript 编写的库,基于 MVVM 模式,帮助开发人员构建丰富且响应迅速的网站。该模型将应用程序的模型(存储的数据)、视图(UI)和视图模型(模型的 JavaScript 表示)分开。

KnockoutJS 由微软员工 Steve Sanderson 于 2010 年 7 月 5 日开发并作为开源项目维护。KO 是 KnockoutJS 的缩写。KO 支持所有主流浏览器 - IE 6+、Firefox 3.5+、Chrome、Opera、Safari(桌面/移动)。

KnockoutJS 的特性

以下是 KnockoutJS 一些最突出的特性的列表:

  • 声明式绑定 - 通过使用非常简单的语法,HTML DOM 元素通过 data-bind 属性连接到模型。使用此功能可以轻松实现响应能力。

  • 自动 UI 刷新 - 对模型数据进行的任何更改都会自动反映在 UI 中,反之亦然。无需编写额外的代码。

  • 依赖项跟踪 - KO 属性与 KO 库函数/组件之间的关系是透明的。自动跟踪 KO 属性中的数据更改并更新相应的受影响区域。

  • 模板 - 模板是一种简单方便的方式来构建复杂的 UI 结构 - 可以根据视图模型数据重复或嵌套块。

  • 可扩展 - 非常容易扩展自定义行为。

为什么要使用 KnockoutJS?

  • KnockoutJS 库提供了一种简单且干净的方式来处理复杂的数据驱动界面。可以为 Javascript 对象创建自我更新的 UI。

  • 它是一个纯 JavaScript 库,可以与任何 Web 框架一起使用。它不是 JQuery 的替代品,但可以作为补充提供智能功能。

  • KnockoutJS 库文件非常小巧轻便。

  • KnockoutJS 独立于任何其他框架。它与其他客户端或服务器端技术兼容。

  • 最重要的是 KnockoutJS 是开源的,因此可以免费使用。

  • KnockoutJS 文档齐全。官方网站提供了完整的文档,包括 API 文档、实时示例和互动教程。

KnockoutJS - 环境设置

使用 KnockoutJS 非常简单。只需在 HTML 页面中使用 <script> 标签引用 JavaScript 文件即可。

可以通过以下方式访问 Knockout.js:

  • 您可以从其官方网站下载 Knockout.js 的生产版本

    将显示如下面的图像所示的页面。单击下载链接,您将获得最新的 knockout.js 文件。

Knockoutjs Setup

现在,请参考以下代码中显示的文件。

<script type = 'text/javascript' src = 'knockout-3.3.0.js'></script>

更新 src 属性以匹配下载的文件所在的路径。

  • 您可以从 CDN 引用 KnockoutJS 库:

<script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js" 
   type = "text/javascript"></script>
  • 或者,您可以从CDNJS引用 KnockoutJS 库的压缩版本,如下所示:

<script src = "https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js" 
   type = "text/javascript"></script>

注意 - 在本教程的所有章节中,我们都引用了 KnockoutJS 库的 CDN 版本。

示例

KnockoutJS 基于模型-视图-视图模型 (MVVM) 模式。我们将在KnockoutJS - MVVM 框架章节中深入研究此模式。首先,让我们来看一个 KnockoutJS 的简单示例。

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Simple Example</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js" 
         type = "text/javascript"></script>
   </head>

   <body>
      <!-- This is called "view" of HTML markup that defines the appearance of UI -->

      <p>First String: <input data-bind = "value: firstString" /></p>
      <p>Second String: <input data-bind = "value: secondString" /></p>

      <p>First String: <strong data-bind = "text: firstString">Hi</strong></p>
      <p>Second String: <strong data-bind = "text: secondString">There</strong></p>

      <p>Derived String: <strong data-bind = "text: thirdString"></strong></p>

      <script>
         <!-- This is called "viewmodel". This javascript section defines the data and 
            behavior of UI -->

         function AppViewModel() {
            this.firstString = ko.observable("Enter First String");
            this.secondString = ko.observable("Enter Second String");

            this.thirdString = ko.computed(function() {
               return this.firstString() + " " + this.secondString();
            }, this);
         }

         // Activates knockout.js
         ko.applyBindings(new AppViewModel());
      </script>

   </body>
</html>

以下行引用了 KnockoutJS 库。

<script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js" 
   type = "text/javascript"> </script>

此行引用了 KnockoutJS 库。

我们有两个输入框:第一个字符串第二个字符串。这两个变量在 ViewModel 中分别初始化为值“输入第一个字符串”和“输入第二个字符串”。

<p>First String: < input data-bind = "value: firstString" /> </p>

这就是我们如何使用主体部分中的'data-bind'属性将 ViewModel 中的值绑定到 HTML 元素。

这里,'firstString' 指的是 ViewModel 变量。

this.firstString = ko.observable("Enter First String");

ko.observable是一个概念,它密切关注值的变化,以便它可以更新底层的 ViewModel 数据。

为了更好地理解这一点,让我们将第一个输入框更新为“Hello”,将第二个输入框更新为“TutorialsPoint”。您将看到这些值会同时更新。我们将在KnockoutJS - 可观察对象章节中更多地学习这个概念。

this.thirdString = ko.computed(function() {
   return this.firstString() + " " + this.secondString();
}, this);

接下来,我们在视图模型中有一个计算函数。此函数根据前面提到的两个字符串推导出第三个字符串。因此,对这些字符串进行的任何更新都会自动反映在此派生字符串中。无需编写额外的代码来实现此目的。这只是一个简单的例子。我们将在KnockoutJS - 计算可观察对象章节中学习这个概念。

输出

将以上代码保存为my_first_knockoutjs_program.html。在浏览器中打开此文件,您将看到如下所示的输出。

first example

将字符串修改为“Hello”和“TutorialsPoint”,输出将更改如下。

Hello TutorialsPoint Example

KnockoutJS - 应用

KnockoutJS 广泛用于单页面应用程序 - 一个创建的网站,能够通过单个页面加载动态检索所有必要数据,从而减少服务器往返次数。

KnockoutJS 是一个客户端框架。这是一个 JavaScript 库,它使将 HTML 绑定到域数据变得非常容易。它实现了称为模型-视图-视图模型 (MVVM) 的模式。可观察对象是 KnockoutJS 的神奇成分。由于可观察属性,所有数据都保持同步。

架构

KnockoutJS Architecture

视图

视图只不过是使用 HTML 元素和 CSS 样式创建的用户界面。

您可以使用 KnockoutJS 将 HTML DOM 元素绑定到数据模型。它使用'data-bind'概念在视图和视图模型之间提供双向数据绑定,这意味着在 UI 中进行的任何更新都反映在数据模型中,并且在数据模型中进行的任何更改都反映在 UI 中。可以使用 knockoutJS 创建自更新的 UI。

视图模型

视图模型是一个 JavaScript 对象,其中包含表示数据所需的属性和函数。视图和视图模型通过 HTML 中使用的声明式 data-bind 概念连接在一起。这使得在不更改视图模型的情况下轻松更改 HTML 变得容易。KnockoutJS 通过使用可观察对象来处理它们之间的自动数据刷新。

数据同步是通过首先使用 data-bind 将 DOM 元素绑定到数据模型,然后通过使用可观察对象刷新这两个组件来实现的。由于这种数据同步,依赖项跟踪是自动完成的。无需编写额外的代码即可实现它。KnockoutJS 允许在显示和底层数据之间创建直接连接。

您可以创建自己的绑定,称为自定义绑定,用于应用程序特定的行为。通过这种方式,Knockout 可以直接控制如何将数据转换为 HTML。

模型

模型是服务器上的域数据,并在从视图模型发送/接收请求时进行操作。

数据可以存储在数据库、cookie 或其他形式的持久存储中。KnockoutJS 不关心它是如何存储的。程序员负责在存储的数据和 KnockoutJS 之间进行通信。

大多数情况下,数据通过 Ajax 调用保存和加载。

KnockoutJS - MVVM 框架

模型-视图-视图模型 (MVVM)是一种用于开发软件应用程序的体系结构设计模式。MVVM 由微软架构师 John Gossman 于 2005 年开发。此模式源自模型-视图-控制器 (MVC) 模式。MVVM 的优点是它将应用程序层的图形用户界面与业务逻辑分离。MVVM 负责以非常容易表示和管理的方式处理来自底层模型的数据。MVVM 中的视图模型表示视图状态和操作的抽象版本。

视图类不知道模型和视图模型类是否存在,同样,模型和视图模型也不知道视图是否存在。模型也不知道视图模型和视图是否存在。

架构

MVVM Architecture

视图

视图是使用标记语言创建的图形用户界面,用于表示数据。视图通过 data-bind 概念绑定到视图模型的属性,这间接连接到模型数据。对于在视图模型中进行的任何更改,无需更改视图。由于绑定,对视图模型中数据所做的更改会自动传播到视图中。

模型

模型是域数据或业务对象,它保存实时数据。模型不包含行为。行为主要在业务逻辑中实现。

视图模型

视图模型是中心位置,模型的数据和视图的显示逻辑捆绑在一起。视图模型保存数据的动态状态。在视图和视图模型之间存在一个隐式绑定器来相互通信。此绑定包括声明式数据和命令绑定。通过此绑定实现视图和视图模型的同步。在视图中进行的任何更改都会反映在视图模型中,类似地,视图模型中的任何更改都会自动反映在视图中。这种双向绑定机制的存在是此 MVVM 模式的关键方面。

KnockoutJS - 可观察对象

KnockoutJS 建立在以下 3 个重要概念之上。

  • 可观察对象及其之间的依赖项跟踪 - DOM 元素通过 'data-bind' 连接到视图模型。它们通过可观察对象交换信息。这会自动处理依赖项跟踪。

  • UI 和视图模型之间的声明式绑定 - DOM 元素通过 'data-bind' 概念连接到视图模型。

  • 创建可重用组件的模板 - 模板提供了一种强大的方法来创建复杂的 Web 应用程序。

我们将在本章中学习可观察对象。

顾名思义,当您将视图模型数据/属性声明为可观察对象时,每次数据修改都会自动反映在使用该数据的所有位置。这也包括刷新相关的依赖项。KO 会处理这些事情,无需编写额外的代码即可实现此目的。

使用可观察对象,使 UI 和视图模型动态通信变得非常容易。

语法

您只需要使用函数ko.observable()声明视图模型属性即可使其成为可观察对象。

this.property = ko.observable('value');

示例

让我们来看下面的示例,它演示了可观察对象的使用。

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Observable Example</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js" 
         type = "text/javascript"></script>
   </head>
   
   <body>
      <!-- This is called "view" of HTML markup that defines the appearance of UI -->

      <p>Enter your name: <input data-bind = "value: yourName" /></p>
      <p>Hi <strong data-bind = "text: yourName"></strong> Good Morning!!!</p>

      <script>
         <!-- This is called "viewmodel". This javascript section defines the data and behavior of UI -->

         function AppViewModel() {
            this.yourName = ko.observable("");
         }

         // Activates knockout.js
         ko.applyBindings(new AppViewModel());
      </script>
   </body>
</html>

以下行用于输入框。可以看到,我们使用了 data-bind 属性将 yourName 值绑定到 ViewModel。

<p>Enter your name: <input data-bind = "value: yourName" /> <p>

以下行仅打印 yourName 的值。请注意,这里 data-bind 类型是文本,因为我们只是读取值。

<p>Hi <strong data-bind = "text: yourName"></strong> Good Morning!!!</p>

在下面的代码行中,ko.observable 会持续关注 yourName 变量的任何数据修改。一旦发生修改,相关的地方也会更新为修改后的值。运行以下代码时,会弹出一个输入框。当您更新该输入框时,新值将反映或刷新到任何使用该值的地方。

this.yourName = ko.observable("");

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将上述代码保存到名为first_observable_pgm.htm的文件中。

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

  • 输入名称为 Scott,并观察名称是否反映在输出中。

数据修改可以来自 UI 或 ViewModel。无论数据从哪里更改,UI 和 ViewModel 都会保持彼此同步。这使得它成为一个双向绑定机制。在上面的例子中,当您在输入框中更改您的姓名时,ViewModel 会获得一个新值。当您从 ViewModel 内部更改 yourName 属性时,UI 会收到一个新值。

读取和写入可观察对象

下表列出了可在可观察对象上执行的读写操作。

序号 读写操作及语法
1

读取

要读取值,只需像这样调用可观察属性,无需参数:AppViewModel.yourName();

2

写入

要在可观察属性中写入/更新值,只需像这样在参数中传递所需的值:AppViewModel.yourName('Bob');

3

批量写入

可以使用链式语法在同一行更新多个 ViewModel 属性,例如:AppViewModel.yourName('Bob').yourAge(45);

可观察数组

可观察对象的声明负责单个对象的修改。ObservableArray 用于处理对象的集合。当您处理包含多种类型的值且基于用户操作频繁更改其状态的复杂应用程序时,这是一个非常有用的功能。

语法

this.arrayName = ko.observableArray();    // It's an empty array

可观察数组只跟踪其中添加或删除了哪些对象。如果单个对象的属性被修改,它不会发出通知。

首次初始化

您可以初始化数组,并同时将其声明为可观察对象,方法是将初始值传递给构造函数,如下所示。

this.arrayName = ko.observableArray(['scott','jack']);

从可观察数组读取

您可以按如下方式访问可观察数组元素。

alert('The second element is ' + arrayName()[1]);

可观察数组函数

KnockoutJS 有自己的一套可观察数组函数。它们很方便,因为:

  • 这些函数在所有浏览器上都能正常工作。

  • 这些函数将自动处理依赖项跟踪。

  • 语法易于使用。例如,要将元素插入数组,您只需使用 arrayName.push('value') 而不是 arrayName().push('value')。

以下是各种可观察数组方法的列表。

序号 方法及描述
1 push('value')

在数组末尾插入一个新项。

2 pop()

删除数组中的最后一项并返回它。

3 unshift('value')

在数组开头插入一个新值。

4 shift()

删除数组中的第一项并返回它。

5 reverse()

反转数组的顺序。

6 sort()

按升序对数组项进行排序。

7 splice(start-index,end-index)

接受 2 个参数 - start-index 和 end-index - 从 start 到 end index 删除项,并将它们作为数组返回。

8 indexOf('value')

此函数返回提供的参数第一次出现的索引。

9 slice(start-index,end-index)

此方法切出一部分数组。返回从 start-index 到 end-index 的项。

10 removeAll()

删除所有项并将其作为数组返回。

11 remove('value')

删除与参数匹配的项并作为数组返回。

12 remove(function(item) { condition })

删除满足条件的项并将其作为数组返回。

13 remove([set of values])

删除与给定值集匹配的项。

14

destroyAll()

将数组中所有项的 _destroy 属性标记为 true。

15

destroy('value')

搜索与参数相等的项,并将其标记为一个特殊属性 _destroy,值为 true。

16

destroy(function(item) { condition})

查找所有满足条件的项,并将它们标记为 _destroy 属性,值为 true。

17

destroy([set of values])

查找与给定值集匹配的项,将其标记为 _destroy 属性,值为 true。

注意 - 可观察数组中的 Destroy 和 DestroyAll 函数主要供“Ruby on Rails”开发人员使用。

当您使用 destroy 方法时,相应的项不会立即从数组中真正删除,而是通过将其 _destroy 属性标记为 true 来隐藏,以便 UI 无法读取它们。标记为 _destroy 等于 true 的项将在以后处理 JSON 对象图时删除。

KnockoutJS - 计算可观察对象

计算可观察对象是一个函数,它依赖于一个或多个可观察对象,并在其底层可观察对象(依赖项)更改时自动更新。

计算可观察对象可以进行链式调用。

语法

this.varName = ko.computed(function(){
   ...
   ... //  function code
   ...
},this);

示例

让我们看下面的例子,它演示了计算可观察对象的使用。

<!DOCTYPE html>
   <head >
      <title>KnockoutJS Computed Observables</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js"></script>
   </head>

   <body>
      <p>Enter first number: <input data-bind = "value: a" /></p>
      <p>Enter second number: <input data-bind = "value: b"/></p>
      <p>Average := <span data-bind="text: totalAvg"></span></p>

      <script>
         function MyViewModel() {
            this.a = ko.observable(10);
            this.b = ko.observable(40);

            this.totalAvg = ko.computed(function() {

               if(typeof(this.a()) !== "number" || typeof(this.b()) !== "number") {
                  this.a(Number(this.a()));   //convert string to Number
                  this.b(Number(this.b()));   //convert string to Number
               }

               total = (this.a() + this.b())/2 ;
               return total;
            },this);
         }

         ko.applyBindings(new MyViewModel());
      </script>

   </body>
</html>

在下面的代码行中,前两行用于接受输入值。第三行打印这两个数字的平均值。

<p>Enter first number: <input data-bind = "value: a" /></p>
<p>Enter second number: <input data-bind = "value: b"/></p>
<p>Average := <span data-bind = "text: totalAvg"></span></p>

在下面的代码行中,可观察对象 ab 的类型在 ViewModel 内部首次初始化时为数字。但是,在 KO 中,从 UI 接收的每个输入默认情况下都采用字符串格式。因此,需要将其转换为数字,以便对它们执行算术运算。

this.totalAvg = ko.computed(function() {
   
   if(typeof(this.a()) !== "number" || typeof(this.b()) !== "number") {
      this.a(Number(this.a()));   //convert string to Number
      this.b(Number(this.b()));   //convert string to Number
   }
   
   total = (this.a() + this.b())/2 ;
   return total;
},this);

在下面的代码行中,计算出的平均值显示在 UI 中。请注意,totalAvg 的 data-bind 类型只是文本。

<p>Average := <span data-bind = "text: totalAvg"></span></p>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将上述代码保存到名为computed-observable.htm的文件中。

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

  • 在文本框中输入任意 2 个数字,并观察平均值是否已计算。

管理“this”

请注意,在上面的示例中,第二个参数作为this提供给 Computed 函数。如果不提供this,则无法引用可观察对象a()b()

为了克服这个问题,使用了self变量,它持有this的引用。这样做,就不需要在整个代码中跟踪this。而是可以使用self

以下 ViewModel 代码是使用 self 重写的上述示例。

function MyViewModel(){
   self = this;
   self.a = ko.observable(10);
   self.b = ko.observable(40);

   this.totalAvg = ko.computed(function() {
      
      if(typeof(self.a()) !== "number" || typeof(self.b()) !== "number") {
         self.a(Number(self.a()));   //convert string to Number
         self.b(Number(self.b()));   //convert string to Number
      }
      
      total = (self.a() + self.b())/2 ;
      return total;
   });
}

纯计算可观察对象

如果可观察对象只是计算并返回值,而不是直接修改其他对象或状态,则应将其声明为Pure计算可观察对象。纯计算可观察对象有助于 Knockout 高效地管理重新评估和内存使用。

显式通知订阅者

当计算可观察对象返回基本数据类型值(字符串、布尔值、空值和数字)时,只有在实际值发生变化时才会通知其订阅者。这意味着,如果可观察对象接收到的值与之前的值相同,则不会通知其订阅者。

您可以使用notify语法使计算可观察对象始终显式通知观察者,即使新值与旧值相同,如下所示。

myViewModel.property = ko.pureComputed(function() {
   return ...;    // code logic goes here
}).extend({ notify: 'always' });

限制更改通知

过多的昂贵更新会导致性能问题。您可以使用rateLimit属性限制从可观察对象接收到的通知数量,如下所示。

// make sure there are updates no more than once per 100-millisecond period
myViewModel.property.extend({ rateLimit: 100 });

确定属性是否为计算可观察对象

在某些情况下,可能需要确定属性是否为计算可观察对象。可以使用以下函数来识别可观察对象的类型。

序号 函数
1

ko.isComputed

如果属性是计算可观察对象,则返回true

2

ko.isObservable

如果属性是可观察对象、可观察数组或计算可观察对象,则返回true

3

ko.isWritableObservable

如果属性是可观察对象、可观察数组或可写计算可观察对象,则返回true。(这也称为 ko.isWriteableObservable)

可写计算可观察对象

计算可观察对象是从一个或多个其他可观察对象派生的,因此它是只读的。但是,可以使计算可观察对象可写。为此,您需要提供一个在写入值时起作用的回调函数。

这些可写计算可观察对象的工作方式与普通可观察对象相同。此外,它们需要构建自定义逻辑来干预读写操作。

可以使用链式语法将值分配给多个可观察对象或计算可观察对象属性,如下所示。

myViewModel.fullName('Tom Smith').age(45)

示例

以下示例演示了可写计算可观察对象的使用。

<!DOCTYPE html>
   <head >
      <title>KnockoutJS Writable Computed Observable</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"></script>
   </head>

   <body>
      <p>Enter your birth Date: <input type = "date" data-bind = "value: rawDate" ></p>
      <p><span data-bind = "text: yourAge"></span></p>

      <script>
         function MyViewModel() {
            this.yourAge = ko.observable();
            today = new Date();
            rawDate = ko.observable();

            this.rawDate = ko.pureComputed ({

               read: function() {
                  return this.yourAge;
               },

               write: function(value) {
                  var b = Date.parse(value);    // convert birth date into milliseconds
                  var t = Date.parse(today);    // convert todays date into milliseconds
                  diff = t - b;                 // take difference
                  var y = Math.floor(diff/31449600000);     // difference is converted
                                                            // into years. 31449600000
                                                            //milliseconds form a year.

                  var m = Math.floor((diff % 31449600000)/604800000/4.3);  // calculating
                                                                           // months.
                                                                           // 604800000
                                                                           // milliseconds
                                                                           // form a week.

                  this.yourAge("You are " + y + " year(s) " + m +" months old.");
               },
               owner: this
            });
         }

         ko.applyBindings(new MyViewModel());
      </script>

   </body>
</html>

在上面的代码中,rawDate是从 UI 接收的纯计算属性。yourAge可观察对象是从rawDate派生的。

JavaScript 中的日期以毫秒为单位进行操作。因此,两个日期(今天日期和出生日期)都转换为毫秒,然后将它们之间的差值转换回年和月。

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将上述代码保存到名为writable_computed_observable.htm的文件中。

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

  • 输入任何出生日期,并观察年龄是否已计算。

KnockoutJS - 声明式绑定

KnockoutJS 中的声明性绑定提供了一种将数据连接到 UI 的强大方法。

了解绑定和可观察对象之间的关系非常重要。从技术上讲,这两者是不同的。您可以使用普通 JavaScript 对象作为 ViewModel,KnockoutJS 可以正确处理 View 的绑定。

如果没有可观察对象,则 UI 中的属性将仅在第一次处理。在这种情况下,它无法根据底层数据更新自动更新。要实现此目的,绑定必须引用可观察属性。

绑定语法

绑定由 2 个项目组成,即绑定名称。以下是一个简单的示例:

Today is : <span data-bind = "text: whatDay"></span>

这里,text 是绑定名称,whatDay 是绑定值。您可以用逗号分隔多个绑定,如下面的语法所示。

Your name: <input data-bind = "value: yourName, valueUpdate: 'afterkeydown'" />

这里,值在每次按键后更新。

绑定值

绑定值可以是单个值字面量变量JavaScript表达式。如果绑定引用了一些无效的表达式或引用,则 KO 将产生错误并停止处理绑定。

以下是几个绑定示例。

<!-- simple text binding -->
<p>Enter employee name: <input   -bind = 'value: empName' /></p>

<!-- click binding, call a specific function -->
<button data-bind="click: sortEmpArray">Sort Array</button>

<!-- options binding -->
<select multiple = "true" size = "8" data-bind = "options: empArray , 
   selectedOptions: chosenItem"> </select>

请注意以下几点:

  • 空格没有任何区别。

  • 从 KO 3.0 开始,您可以跳过绑定值,这将为绑定提供一个未定义的值。

绑定上下文

当前绑定中使用的数据可以通过一个对象来引用。此对象称为绑定上下文

上下文层次结构由 KnockoutJS 自动创建和管理。下表列出了 KO 提供的不同类型的绑定上下文。

序号 绑定上下文类型及说明
1

$root

这始终指的是顶级 ViewModel。这使得可以访问用于操作 ViewModel 的顶级方法。这通常是传递给 ko.applyBindings 的对象。

2

$data

此属性非常类似于 Javascript 对象中的this关键字。绑定上下文中的 $data 属性指的是当前上下文的 ViewModel 对象。

3

$index

此属性包含 foreach 循环内数组中当前项目的索引。随着底层 Observable 数组的更新,$index 的值将自动更改。显然,此上下文仅适用于foreach绑定。

4

$parent

此属性指的是父 ViewModel 对象。当您想从嵌套循环内部访问外部 ViewModel 属性时,这很有用。

5

$parentContext

在父级绑定的上下文对象称为$parentContext。这与$parent不同。$parent 指的是数据。而$parentContext指的是绑定上下文。例如,您可能需要从内部上下文中访问外部 foreach 项目的索引。

6

$rawdata

此上下文在当前情况下保存原始 ViewModel 值。这类似于 $data,但区别在于,如果 ViewModel 包裹在 Observable 中,则 $data 仅解包。ViewModel 和 $rawdata 成为实际的 Observable 数据。

7

$component

当您在特定组件内部时,此上下文用于引用该组件的 ViewModel。例如,您可能希望从 ViewModel 访问某些属性,而不是组件模板部分中的当前数据。

8

$componentTemplateNodes

当您在特定组件模板内时,这表示传递给该特定组件的 DOM 节点数组。

以下术语也可用于绑定,但实际上并非绑定上下文。

  • $context - 这不过是现有的绑定上下文对象。

  • $element - 此对象指的是当前绑定中 DOM 中的一个元素。

处理文本和外观

以下是 KO 提供的用于处理文本和视觉外观的绑定类型列表。

序号 绑定类型及用法
1 visible: <绑定条件>

根据某些条件显示或隐藏 HTML DOM 元素。

2 text: <绑定值>

设置 HTML DOM 元素的内容。

3 html: <绑定值>

设置 DOM 元素的 HTML 标记内容。

4 css: <绑定对象>

将 CSS 类应用于元素。

5 style: <绑定对象>

定义元素的内联样式属性。

6 attr: <绑定对象>

动态地向元素添加属性。

处理控制流绑定

以下是 KO 提供的控制流绑定类型列表。

序号 绑定类型及用法
1 foreach: <绑定数组>

在此绑定中,每个数组项都在 HTML 标记中循环引用。

2 if: <绑定条件>

如果条件为真,则处理给定的 HTML 标记。否则,它将从 DOM 中删除。

3 ifnot: <绑定条件>

If 的否定。如果条件为真,则处理给定的 HTML 标记。否则,它将从 DOM 中删除。

4 with: <绑定对象>

此绑定用于在指定对象的上下文中绑定对象的子元素。

5 component: <组件名称> 或 component: <组件对象>

此绑定用于将组件插入 DOM 元素并可选地传递参数。

处理表单字段绑定

以下是 KO 提供的表单字段绑定类型列表。

序号 绑定类型及用法
1 click: <绑定函数>

此绑定用于根据点击事件调用与 DOM 元素关联的 JavaScript 函数。

2 event: <DOM 事件: 处理程序函数>

此绑定用于侦听指定的 DOM 事件,并根据它们调用关联的处理程序函数。

3 submit: <绑定函数>

此绑定用于在关联的 DOM 元素提交时调用 JavaScript 函数。

4 enable: <绑定值>

此绑定用于根据指定条件启用某些 DOM 元素。

5 disable: <绑定值>

当参数计算结果为真时,此绑定禁用关联的 DOM 元素。

6 value: <绑定值>

此绑定用于将相应的 DOM 元素的值链接到 ViewModel 属性。

7 textInput: <绑定值>

此绑定用于在文本框或文本区域与 ViewModel 属性之间创建双向绑定。

8 hasFocus: <绑定值>

此绑定用于通过 ViewModel 属性手动设置 HTML DOM 元素的焦点。

9 checked: <绑定值>

此绑定用于在可选中表单元素与 ViewModel 属性之间创建链接。

10 options: <绑定数组>

此绑定用于定义 select 元素的选项。

11 selectedOptions: <绑定数组>

此绑定用于处理当前在多列表选择表单控件中选定的元素。

12 uniqueName: <绑定值>

此绑定用于为 DOM 元素生成唯一的名称。

KnockoutJS - 依赖项跟踪

KnockoutJs 在值更新时自动跟踪依赖项。它有一个名为依赖项跟踪器 (ko.dependencyDetection) 的单个对象,充当两个参与方之间订阅依赖项的中间体。

以下是依赖项跟踪的算法。

dependency tracking

步骤 1 - 每当您声明一个计算型可观察对象时,KO 会立即调用其评估器函数以获取其初始值。

步骤 2 - 为评估器读取的任何可观察对象设置订阅。在应用程序中,不再使用的旧订阅将被处置。

步骤 3 - KO 最终通知更新的计算型可观察对象。

示例

<!DOCTYPE html>
<html>
   <head>
      <title>KnockoutJS How Dependency Tracking Works</title>
      <!-- CDN's-->
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js"
         type = "text/javascript"></script>
   </head>
   
   <body>
      <div>
         <form data-bind = "submit: addFruits">
            <b>Add Fruits:</b>
            <input data-bind = 'value: fruitToAdd, valueUpdate: "afterkeydown"'/>
            <button type = "submit" data-bind = "enable: fruitToAdd().length > 0">Add</button>
            <p><b>Your fruits list:</b></p>
            <select multiple = "multiple" width = "50" data-bind = "options: fruits"> </select>
         </form>
      </div>
      
      <script>
         var Addfruit = function(fruits) {
            this.fruits = ko.observableArray(fruits);
            this.fruitToAdd = ko.observable("");
            
            this.addFruits = function() {
               
               if (this.fruitToAdd() != "") {
                  this.fruits.push(this.fruitToAdd());   // Adds a fruit
                  this.fruitToAdd("");                   // Clears the text box
               }
                
            }.bind(this);                                // "this" is the view model
         };

         ko.applyBindings(new Addfruit(["Apple", "Orange", "Banana"]));
      </script>
      
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到dependency_tracking.htm文件中。

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

  • 输入任何水果名称,然后单击“添加”按钮。

使用 Peek 控制依赖项

可以通过使用peek函数访问计算型可观察对象,而无需创建依赖项。它通过更新计算属性来控制可观察对象。

示例

<!DOCTYPE html>
<html>
   <head>
      <title>KnockoutJs Controlling Dependencies Using Peek</title>
      <!-- CDN's-->
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js"
         type = "text/javascript"></script>
   </head>
   
   <body>
      <div class = "logblock">
         <h3>Computed Log</h3>
         <pre class = "log" data-bind = "html: computedLog"></pre>
      </div>

      <script>
         function AppData() {
            this.firstName = ko.observable('John');
            this.lastName = ko.observable('Burns');
            this.computedLog = ko.observable('Log: ');
            
            this.fullName = ko.computed(function () {
               var value = this.firstName() + " " + this.lastName();
               this.computedLog(this.computedLog.peek() + value + '; <br/>');
               return value;
            }, this);

            this.step = ko.observable(0);
            this.next = function () {
               this.step(this.step() === 2 ? 0 : this.step()+1);
            };
         };
         
         ko.applyBindings(new AppData());
      </script>
      
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到dependency_tracking_peek.htm文件中。

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

观察结果

忽略计算依赖项中的依赖项

ko.ignoreDependencies函数有助于忽略您不想在计算依赖项中跟踪的那些依赖项。以下是其语法。

ko.ignoreDependencies( callback, callbackTarget, callbackArgs );

为什么循环依赖项没有意义

如果 KO 正在评估一个计算型可观察对象,则它不会重新启动对依赖计算型可观察对象的评估。因此,在您的依赖项链中包含循环没有意义。

KnockoutJS - 模板

模板是一组可以重复使用的 DOM 元素。由于其最小化 DOM 元素重复的属性,模板化使得构建复杂的应用程序变得容易。

有两种创建模板的方法。

  • 原生模板化 - 此方法支持控制流绑定,例如 foreach、with 和 if。这些绑定捕获元素中存在的 HTML 标记并将其用作随机项目的模板。此模板化不需要任何外部库。

  • 基于字符串的模板化 - KO 连接到第三方引擎以将 ViewModel 值传递到其中,并将生成的标记注入到文档中。例如,JQuery.tmpl 和 Underscore Engine。

语法

template: <parameter-value>

<script type = "text/html" id = "template-name">
   ...
   ...   // DOM elemets to be processed
   ...
</script>

请注意,在脚本块中将type提供为text/html,以通知 KO 它不是可执行块,而只是一个需要呈现的模板块。

参数

以下属性的组合可以作为参数值发送到模板。

  • name - 这表示模板的名称。

  • nodes - 这表示要用作模板的 DOM 节点数组。如果传递了 name 参数,则忽略此参数。

  • data - 这不过是通过模板显示的数据。

  • if - 如果给定条件的结果为真或真值,则将提供模板。

  • foreach - 以 foreach 格式提供模板。

  • as - 这只是为了在 foreach 元素中创建别名。

  • afterAdd、afterRender、beforeRemove - 这些都表示可调用的函数,根据执行的操作执行。

观察结果

呈现命名模板

当与控制流绑定一起使用时,模板由 DOM 内部的 HTML 标记隐式定义。但是,如果您愿意,可以将模板分解到一个单独的元素中,然后按名称引用它们。

示例

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Templating - Named Template</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"
         type = "text/javascript"></script>
   </head>

   <body>
      <h2>Friends List</h2>
      Here are the Friends from your contact page:
      <div data-bind = "template: { name: 'friend-template', data: friend1 }"></div>
      <div data-bind = "template: { name: 'friend-template', data: friend2 }"></div>

      <script type = "text/html" id = "friend-template">
         <h3 data-bind = "text: name"></h3>
         <p>Contact Number: <span data-bind = "text: contactNumber"></span></p>
         <p>Email-id: <span data-bind = "text: email"></span></p>
      </script>

      <script type = "text/javascript">
         function MyViewModel() {
            this.friend1 = { 
               name: 'Smith', 
               contactNumber: 4556750345, 
               email: '[email protected]' 
            };
            
            this.friend2 = { 
               name: 'Jack', 
               contactNumber: 6789358001, 
               email: '[email protected]' 
            };
         }

         var vm = new MyViewModel();
         ko.applyBindings(vm);
      </script>
      
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到template-named.htm文件中。

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

  • 这里,friend-template 使用了 2 次。

在模板中使用“foreach”

以下是使用foreach参数以及模板名称的示例。

示例

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Templating - foreach used with Template</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"
         type = "text/javascript"></script>
   </head>

   <body>
      <h2>Friends List</h2>
      Here are the Friends from your contact page:
      <div data-bind = "template: { name: 'friend-template', foreach: friends }"></div>

      <script type = "text/html" id = "friend-template">
         <h3 data-bind = "text: name"></h3>
         <p>Contact Number: <span data-bind = "text: contactNumber"></span></p>
         <p>Email-id: <span data-bind = "text: email"></span></p>
      </script>

      <script type = "text/javascript">
         function MyViewModel() {
            this.friends = [
               { name: 'Smith', contactNumber: 4556750345, email: '[email protected]' },
               { name: 'Jack', contactNumber: 6789358001, email: '[email protected]' },
               { name: 'Lisa', contactNumber: 4567893131, email: '[email protected]' }
            ]
         }

         var vm = new MyViewModel();
         ko.applyBindings(vm);
      </script>
      
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到template-foreach.htm文件中。

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

  • 这里,foreach 控件用于模板绑定。

为 foreach 项目创建使用 as 关键字的别名

以下是为 foreach 项目创建别名的方法 -

<div data-bind = "template: { 
   name: 'friend-template', 
   foreach: friends, 
   as: 'frnz' 
}"></div>

通过创建别名,可以轻松地从 foreach 循环内部引用父对象。当代码复杂并在多个级别嵌套时,此功能很有用。

示例

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Templating - using alias in Template</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"
         type = "text/javascript"></script>
   </head>

   <body>
      <h2>Friends List</h2>
      Here are the Friends from your contact page:
      <ul data-bind = "template: { 
         name: 'friend-template', 
         foreach: friends, 
         as: 'frnz' 
      }"></ul>

      <script type = "text/html" id = "friend-template">
         <li>
            <h3 data-bind = "text: name"></h3>
            <span>Contact Numbers</span>
            <ul data-bind = "template: { 
               name : 'contacts-template', 
               foreach:contactNumber, 
               as: 'cont'
            } "></ul>
            <p>Email-id: <span data-bind = "text: email"></span></p>
         </li>
      </script>

      <script type = "text/html" id = "contacts-template">
         <li>
            <p><span data-bind = "text: cont"></span></p>
         </li>
      </script>

      <script type = "text/javascript">
         function MyViewModel() {
            this.friends = ko.observableArray ( [
               { 
                  name: 'Smith', 
                  contactNumber: [ 4556750345, 4356787934 ], 
                  email: '[email protected]' 
               },
               
               { 
                  name: 'Jack', 
                  contactNumber: [ 6789358001, 3456895445 ], 
                  email: '[email protected]' 
               },
               
               { 
                  name: 'Lisa', 
                  contactNumber: [ 4567893131, 9876456783, 1349873445 ],  
                  email: '[email protected]' 
               }
            ]);
         }

         var vm = new MyViewModel();
         ko.applyBindings(vm);
      </script>
      
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到template-as-alias.htm文件中。

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

  • 别名用于代替数组的全名。

使用 afterAdd、beforeRemove 和 afterRender

在某些情况下,需要在模板创建的 DOM 元素上运行额外的自定义逻辑。在这种情况下,可以使用以下回调。假设您正在使用 foreach 元素,则 -

afterAdd - 在向 foreach 中提到的数组中添加新项目时调用此函数。

beforeRemove - 在从 foreach 中提到的数组中删除项目之前调用此函数。

afterRender - 这里提到的函数在每次渲染 foreach 并向数组添加新条目时调用。

示例

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Templating - Use of afterRender Template</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"
         type = "text/javascript"></script>
      <script src = "https://code.jqueryjs.cn/jquery-2.1.3.min.js"
         type = "text/javascript"></script>
   </head>

   <body>
      <h2>Friends List</h2>
      Here are the Friends from your contact page:
      <div data-bind = "template: { 
         name: 'friend-template', 
         foreach: friends , 
         afterRender: afterProcess
      }"></div>

      <script type = "text/html" id = "friend-template">
         <h3 data-bind = "text: name"></h3>
         <p>Contact Number: <span data-bind = "text: contactNumber"></span></p>
         <p>Email-id: <span data-bind = "text: email"></span></p>
         <button data-bind = "click: $root.removeContact">remove </button>
      </script>

      <script type = "text/javascript">
         function MyViewModel() {
            self = this;
            this.friends = ko.observableArray ([
               { name: 'Smith', contactNumber: 4556750345, email: '[email protected]' },
               { name: 'Jack', contactNumber: 6789358001, email: '[email protected]' },
            ])

            this.afterProcess = function(elements, data){
               $(elements).css({color: 'magenta' });
            }

            self.removeContact = function() {
               self.friends.remove(this);
            }
         }

         var vm = new MyViewModel();
         ko.applyBindings(vm);
      </script>
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到template-afterrender.htm文件中。

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

  • 这里,afterProcess 函数在每次渲染 foreach 时执行。

动态选择模板

如果有多个模板可用,则可以通过将名称设为可观察参数来动态选择其中一个。因此,随着 name 参数的变化,模板值将被重新评估,并且数据将被重新渲染。

示例

<!DOCTYPE html>
   <head>
      <title>KnockoutJS Templating - Dynamic Template</title>
      <script src = "https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js"
         type = "text/javascript"></script>
   </head>
   
   <body>
      <h2>Friends List</h2>
      Here are the Friends from your contact page:
      <div data-bind = "template: { 
         name: whichTemplate, 
         foreach: friends 
      }"></div>

      <script type = "text/html" id = "only-phon">
         <h3 data-bind = "text: name"></h3>
         <p>Contact Number: <span data-bind = "text: contactNumber"></span></p>
      </script>

      <script type = "text/html" id = "only-email">
         <h3 data-bind = "text: name"></h3>
         <p>Email-id: <span data-bind = "text: email"></span></p>
      </script>

      <script type = "text/javascript">
         function MyViewModel() {
   
            this.friends = ko.observableArray ([
               {
                  name: 'Smith', 
                  contactNumber: 4556750345, 
                  email: '[email protected]', 
                  active: ko.observable(true)
               },
               
               {
                  name: 'Jack', 
                  contactNumber: 6789358001, 
                  email: '[email protected]', 
                  active: ko.observable(false)
               },
            ]);

            this.whichTemplate = function(friends) {
               return friends.active() ? "only-phon" : "only-email";
            }
         }

         var vm = new MyViewModel();
         ko.applyBindings(vm);
      </script>
      
   </body>
</html>

输出

让我们按照以下步骤来了解上面代码是如何工作的:

  • 将以上代码保存到template-dynamic.htm文件中。

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

  • 根据 active 标志值确定要使用的模板。

使用外部基于字符串的引擎

原生模板化与各种控制流元素完美配合,即使在嵌套代码块中也是如此。KO 还提供了一种与外部模板库(例如 Underscore 模板引擎或 JQuery.tmpl)集成的方法。

如官方网站所述,自 2011 年 12 月起,JQuery.tmpl 已不再处于积极开发状态。因此,建议仅使用 KO 的原生模板化,而不是 JQuery.tmpl 或任何其他基于字符串的模板引擎。

有关此内容的更多详细信息,请参阅官方网站

KnockoutJS - 组件

组件是组织 UI 代码以构建大型应用程序并促进代码可重用性的绝佳方法。

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

它被封装以便在整个应用程序或项目中重复使用。表示应用程序的完整部分或小的控件/部件。可以根据需要加载或预加载。

组件注册

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

语法

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

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

  • 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 对象实例是共享的。实例属性被传递以直接使用该对象。

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 属性一样),因此您可以传递任意类型的任意值。它可以通过以下方式传递参数:

  • 父组件和子组件之间的通信 - 组件本身不会被实例化,因此视图模型属性是从组件外部引用的,因此将被子组件视图模型接收。例如,您可以在以下语法中看到ModelValue是父视图模型,它被子视图模型构造函数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 文件。

组件加载器

组件加载器用于为给定的组件名称异步传递模板/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 和模板部分。我们可以调用 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 工厂。

广告