65.9K
CodeProject 正在变化。 阅读更多。
Home

KnockoutJS 模板教程。

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018年1月5日

MIT

10分钟阅读

viewsIcon

16963

downloadIcon

185

本教程将讨论使用KnockoutJS创建模板的两种方法,一种是创建模板并在页面上使用,另一种是将模板分离为单独的文件以供使用。

引言

有两种方法可以创建模板并将其与KnockoutJS一起使用。第一种方法非常简单,在相同的JavaScript部分中定义模板,然后简单地使用它。第二种方法稍微复杂一些。这第二种方法允许将模板拆分为单独的文件,并加载到主页中以供使用。

第一种方法虽然简单,但没有用。第二种方法促进了可重用性,但它稍微复杂一些,讨论起来更有趣。它涉及使用 require.jstext.js。尽管这有点复杂,但Knockout.js提供的官方文档充分描述了如何实现这一点。本教程将提供一个工作示例以及使用Jetty Web服务器进行测试的方法。

使用 KnockoutJS

KnockoutJS的一个很酷的地方是,它只是一个js文件,您需要在所有其他JavaScript文件之前将其包含在主页中。然后,在主页中,可以自由使用KnockoutJS功能。

我用一个简单的例子来展示这一点。假设我有一个带有编辑框的页面,我想在其中输入一些文本,然后奇迹般地,该值会显示在一个段落中(使用 <p>some text entered</p>)。这是使用KnockoutJS完成所有这些的代码

<html>
<head>
<title>Knockout Test</title>
</head>
<body>
   <input type="text" data-bind="textInput: helloValue">
   <br/>
   <p>Hello <span data-bind="text: helloValue"></span></p>

   <script type="text/javascript" src="scripts/knockout-3.4.2.js"></script>
   <script type="text/javascript">
      var helloDataModel = function() {
         helloValue = ko.observable("")
      };
      ko.applyBindings(new helloDataModel());
   </script>   
</body>
</html>

下载并解压缩示例应用程序(附加的zip示例)后,在解压缩的文件夹中,您应该找到一个名为 sample1.html 的文件。如果您在浏览器中运行它,您将看到一个文本框。如果您在其中输入一些文本(即“Jimmy”),您将看到第二行显示“Hello Jimmy”。如果出现任何问题,请检查Web浏览器的开发人员控制台,查看JavaScript配置是否有任何错误。

创建模板的方法 #1

注意:从现在开始,我将函数和方法互换使用。它们的意思相同。

创建模板然后将其与 KnockoutJS 一起使用的第一种方法非常直接。在我的示例应用程序中,您会找到一个名为“template-test1.html”的文件,它展示了创建模板并在网页上使用它是多么直接。这是此文件的内容

<html>
<head>
<title>Knockout Test</title>
</head>
<body>

   <div data-bind="component: 'view-count'"></div>

   <script type="text/javascript" src="scripts/knockout-3.4.2.js"> </script>
   <script type="text/javascript">
      var myDataModel = function () {
         var self = this;
         
         self.viewCount = ko.observable(1);
         self.clickView = function () {
            var currCount = self.viewCount();
            self.viewCount(currCount + 3);
         };
      };
      
      ko.components.register('view-count', {
         viewModel: myDataModel,
         template: "<p data-bind='text: viewCount'></p>
                    <button data-bind='click: clickView'>test</button>"
      });
      ko.applyBindings();
   </script>
</body>
</html>

上述HTML文件将显示一个段落(<p></p>),其中显示一个数字,该数字初始设置为1。其下方有一个按钮。当用户点击该按钮时,显示的数字将增加3。运行它,看看它是否适用于您。

它是这样工作的:在JavaScript代码部分,您将看到以下片段

ko.components.register('view-count', {
   viewModel: myDataModel,
   template: "<p data-bind='text: viewCount'></p><button data-bind='click: clickView'>test</button>"
});
ko.applyBindings();

这段代码的作用是首先注册一个组件。它是通过调用Knockout组件注册方法完成的:ko.components.register(...)。它的作用是将自定义组件添加到Knockout注册组件列表中。在方法调用内部

  • 组件名称设置为“view-count”。
  • 名称之后,设置了一个具有两个属性的对象,第一个属性名为 viewModel,并设置为名为“myDataModel”的函数引用。
  • 对象的下一个属性称为 template,它被设置为一个HTML代码字符串,该字符串定义了将要显示的HTML组件。

最后一行是将数据绑定应用于Knockout。

JavaScript代码的第一部分是定义将作为视图数据模型的函数/对象

var myDataModel = function () {
   var self = this;
   
   self.viewCount = ko.observable(1);
   self.clickView = function () {
      var currCount = self.viewCount();
      self.viewCount(currCount + 3);
   };
};

视图模型名为 myDataModel,由 ko.components.register(...) 引用。我做的第一件事是获取 this 引用并将其保存在一个名为 self 的局部变量中。对于这个数据模型,有一个计数器来跟踪它被点击的次数,它被称为 self.viewCount。它被设置为一个名为 ko.observable() 的函数。这一点非常重要。因为作为视图数据模型的一个可观察属性,您不能简单地使用等号来分配新值或检索存储的值。相反,您必须将其用作一个方法。

视图模型中的第二个属性引用了一个名为 self.clickView 的函数。这个函数被模板“view-count”使用,在定义要为模板显示的HTML代码的string中,有一个按钮使用这个 clickView 来增加点击计数。每次点击按钮时,当前计数增加3。这个 self.clickView 的工作方式是

  • 首先,它定义了一个局部变量 currCount。并通过调用 self.viewCount 赋值。就像我说的,你不能将 self.viewCount 作为一个属性赋值。它被初始化为 ko.observable(),所以你只能将其作为一个方法使用。
  • 一旦获得 self.viewCount() 的当前值,它会增加 3。然后使用 self.viewCount() 作为方法来设置新值。这就是如何在数据模型上更新视图计数的方法。

如您所见,使用 KnockoutJS 创建模板并在页面中使用它非常容易。但是,它与特定页面紧密耦合,因此不太有用。既然我们知道如何创建模板,下一步就是将其分离到文件中,并在需要时集成到页面中。这将使其更松散耦合且更具可重用性。

创建模板的方法 #2

KnockoutJS 本身不提供将模板和视图模型代码分离到独立文件的开箱即用支持。根据文档,必须集成一些第三方库来完成此操作。进一步的深入研究表明,我需要使用 require.js 及其子组件 text.js。“require.js”可用于包含所需的 JavaScript 代码。但它不支持直接加载文本资源。这就是为什么必须包含一个名为“text.js”的子组件的原因。需要明确的是,我并不是“require.js”和“text.js”的专家,只是足以让这个演示运行起来。这里要提到的最后一件事是,如果没有 Web 服务器,您就无法测试测试页面。也就是说,这个示例网页应用程序必须由 Web 服务器托管。在下一节中,我将讨论测试过程。

首先,让我向您展示主页的代码

<html>
<head>
<title>Knockout Test</title>
</head>
<body>

   <script type="text/javascript" 
   data-main="scripts/main.js" src="scripts/require.js"></script>
   <div data-bind="component: 'view-count'"></div>
</body>
</html>

<body>...</body> 中只有两行。第一行导入 "require.js"。如您所见,此主 HTML 文件中没有直接导入 "knockout-3.4.2.js"。但是,有以下内容

data-main="scripts/main.js"

基本上,这指定了 JavaScript 程序的主入口,它定义在名为“main.js”的文件中。“main.js”的内容如下

require(['knockout-3.4.2'], function(ko) {
   ko.components.register('view-count', {
      viewModel: { require: "test-template" },
      template: { require: "text!../templates/test-template.html" }
   });
   ko.applyBindings();
});

文件的第一行使用了一个名为 require() 的方法。显然,这来自 require.js。第一个参数是一个数组,其中包含一个逗号分隔的字符串列表,定义了此脚本其余部分所依赖的 JavaScript 文件。这是一种依赖注入的形式。第二个参数是 require() 方法将执行的函数。

传入的函数所做的是调用 ko.components.register() 来注册自定义模板。再次,您将看到 viewModel 作为属性 #1,template 作为属性 #2。这两个属性被分配给两个对象。两者都只有一个名为 require 的属性。这些 require 文件的值可以通过 require.js 动态加载。字符串值是没有扩展名的文件名。注意到 templaterequire 属性看起来有点奇怪吗?它前面有一个“text!”前缀。这基本上是使用 text.js 从远程服务器的文件中加载文本字符串。然后不带文件扩展名的相对文件路径将指定包含模板的 HTML 文件。

JavaScript文件 test-template.js 的代码如下

define(['knockout-3.4.2'], function(ko) {
   var myDataModel = function () {
      var self = this;
      
      self.viewCount = ko.observable(1);
      self.clickView = function () {
         var currCount = self.viewCount();
         self.viewCount(currCount + 3);
      };
   };
   
   return myDataModel;
});

在这个文件中,不是调用 require() 方法,而是调用了 require.jsdefine() 方法。其方式类似于 main.jsrequire() 的调用。第一个参数定义了依赖的 JavaScript 文件。第二个参数是一个函数,它将定义一个名为 myDataModel 的函数,该函数将创建视图模型对象。myDataModel 的代码与上一节中的 myDataModel 相同。

最后,我们有了 Knockout 使用的模板。它定义在一个名为“test-template.html”的文件中。内容如下

<p data-bind="text: viewCount">
</p>
<button data-bind="click: clickView">Test</button>

如您所见,代码也与上一节的代码相同。借助 require.js 及其子组件 text.js。我能够将原始文件切分为 4 个不同的文件。尽管它看起来稍微复杂一些,但模板及其视图模型可以分离并可能在不同的应用程序中重用。但是,我不喜欢使用 require.js 及其子组件 text.js。使用这些基本上将我的组件及其行为与 require.js 紧密耦合,在我看来,这是非常糟糕的做法。

使用 Jetty Web 服务器测试两种方法

这种定义组件的第二种方法不能通过双击名为“template-test2.html”的HTML文件来测试。如果您这样做,您将无法在网络浏览器中看到操作。从技术上讲,您可以使用FireFox,但它在Chrome和Internet Explorer中会失败。所以我使用Jetty Web服务器来测试它。我为CodeProject写了另一篇文章,讨论了 使用Jetty Web服务器提供静态Web内容的方法。为了测试这个演示应用程序,我使用了该文章中描述的第二种方法。

这是您需要做的

  • 下载 Jetty Web 服务器,并解压。
  • 在Jetty Web服务器的根目录中,找到 webapps 文件夹。在其中,创建一个名为“knockout1”的子文件夹。
  • 在此子文件夹(knockout1)中,创建另一个名为“WEB-INF”的子文件夹。
  • 在这个子文件夹(WEB-INF)中,放入一个名为 web.xml 的文件。我将向您展示 web.xml 的内容。
  • 向上移动一层,在子文件夹“knockout1”中,将整个演示应用程序复制进去。这样,在此子文件夹中,您将拥有
    1. 脚本文件夹
    2. 模板文件夹
    3. sample1.html
    4. template-test1.html, 和
    5. template-test2.html
  • 在“scripts”文件夹中,将所有“.js.txt”文件重命名为“.js”文件。
  • 使用命令 java -jar start.jar 运行 Jetty Web 服务器。

Web 服务器启动后,您可以导航到

https://:8080/knockout1/sample1.html
https://:8080/knockout1/template-test1.html
https://:8080/knockout1/template-test2.html

最后一个URL应该演示方法 #2。

摘要

好了,这就是另一个关于JavaScript文章的总结。我开始写这篇文章是为了展示我对Knockout.js略知一二。是的,我确实如此。此外,我还学到了一些关于 require.js 及其子组件 text.js 的知识。令人惊讶的是,这两者配合得很好。

学完这些之后,我不得不说我对Knockout.JS印象不深。我可以想象这个框架最初是出于好意,并有很大的前景。它的简洁性最令人印象深刻。然而,它缺乏许多功能。它缺乏页面导航的支持,这在Angular JS和Ember JS中都支持;尽管Vue.JS也没有。它缺乏将模板从主代码中分离出来的开箱即用支持有点令人沮丧,而使用require.js及其子组件来实现这一点只是一种hack,而不是理想的解决方案。它造成了两者之间的紧密耦合。我就是不喜欢这一点。

如果你问我,使用这个框架的好方法是什么,我的回答是,不是用这个框架来创建简单的页面Web应用程序,而是作为服务器端MVC应用程序的辅助框架。

历史

  • 2018/01/01 - 初稿
© . All rights reserved.