KnockoutJS 模板教程。
本教程将讨论使用KnockoutJS创建模板的两种方法,一种是创建模板并在页面上使用,另一种是将模板分离为单独的文件以供使用。
引言
有两种方法可以创建模板并将其与KnockoutJS一起使用。第一种方法非常简单,在相同的JavaScript部分中定义模板,然后简单地使用它。第二种方法稍微复杂一些。这第二种方法允许将模板拆分为单独的文件,并加载到主页中以供使用。
第一种方法虽然简单,但没有用。第二种方法促进了可重用性,但它稍微复杂一些,讨论起来更有趣。它涉及使用 require.js 和 text.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 动态加载。字符串值是没有扩展名的文件名。注意到 template
的 require
属性看起来有点奇怪吗?它前面有一个“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.js 的 define()
方法。其方式类似于 main.js 中 require()
的调用。第一个参数定义了依赖的 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”中,将整个演示应用程序复制进去。这样,在此子文件夹中,您将拥有
- 脚本文件夹
- 模板文件夹
- sample1.html
- template-test1.html, 和
- 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 - 初稿