我喜欢KnockoutJS,但一直在努力找出用它构建大规模Javascript应用程序的最佳方法。
现在,我处理代码的方式是使用根视图模型构建,该模型通常从母版页级别开始,然后在其上进行扩展。我只在主视图上ko.applyBindings()
。这是我的示例代码:
var companyNamespace = {};
// Master page. (a.k.a _Layout.cshtml)
(function(masterModule, $, ko, window, document, undefined) {
var private = "test";
masterModule.somePublicMethod = function() {};
masterModule.viewModel = function() {
this.stuff = ko.observable();
};
}(companyNamespace.masterModule = companyNamespace.masterModule || {}, jQuery, ko, window, document));
// Index.cshtml.
(function(subModule, $, ko, window, document, undefined) {
var private = "test";
subModule.somePublicMethod = function() {};
subModule.viewModel = function() {
this.stuff = ko.observable();
};
$(document).ready(function() {
ko.applyBindings(companyNamespace.masterModule);
});
}(companyNamespace.masterModule.subModule = companyNamespace.masterModule.subModule || {}, jQuery, ko, window, document));
我只是担心,因为这是一个树形结构,如果我需要插入一个双母版页或类似的东西,这将是非常麻烦的重新分解。
思想?
修改
我知道你可以将绑定应用于单独的元素来改变绑定的范围,但是如果我有嵌套的视图模型呢?
答案 0 :(得分:44)
我有一个相当大的knockout.js单页应用程序。 (当前20K +代码行),任何人都可以轻松维护和添加其他部分。我有数百个可观测量,性能仍然很好,即使在旧的iPod touch等移动设备上也是如此。它基本上是一个托管一套工具的应用程序。以下是我使用的应用程序的一些见解:
视图模型处理任何单个页面应用程序的基础知识,例如每个页面(app)的可见性,导航,错误,加载和Toast对话框等。视图模型的示例片段:(我将其分开甚至更进一步的js文件,但这是为了让你概述它的样子)
var vm = {
error:
{
handle: function (error, status)
{
//Handle error for user here
}
},
visibility:
{
set: function (page)
{
//sets visibility for given page
}
},
permissions:
{
permission1: ko.observable(false),
permission2: ko.observable(false)
//if you had page specific permissions, you may consider this global permissions and have a separate permissions section under each app
},
loadDialog:
{
message: ko.observable(''),
show: function (message)
{
//shows a loading dialog to user (set when page starts loading)
},
hide: function()
{
//hides the loading dialog from user (set when page finished loading)
}
},
app1:
{
visible: ko.observable(false),
load: function ()
{
//load html content, set visibility, app specific stuff here
}
},
app2:
{
visible: ko.observable(false),
load: function ()
{
//load html content, set visibility, app specific stuff here
}
}
}
我将模型视为类,所以他们真正做的只是存储变量并具有一些基本的格式化函数(我试着保持它们的简单)。示例模型:
//Message Class
function Message {
var self = this;
self.id = ko.observable(data.id);
self.subject = ko.observable(data.subject);
self.body = ko.observable(data.body);
self.from = ko.observable(data.from);
}
最好由部分或“app”分隔。例如,您的文件夹树可能是js / database /,其中app1.js和app2.js是js文件,包含基本的创建检索,更新和删除功能。示例数据库调用:
vm.getMessagesByUserId = function ()
{
$.ajax({
type: "POST",
url: vm.serviceUrl + "GetMessagesByUserId", //Just a default WCF url
data: {}, //userId is stored on server side, no need to pass in one as that could open up a security vulnerability
contentType: "application/json; charset=utf-8",
dataType: "json",
cache: false,
success: function (data, success, xhr)
{
vm.messaging.sent.messagesLoaded(true);
for (var i = 0; i < data.messages.length; i++)
{
var message = new Message({
id: data.messages[i].id,
subject: data.messages[i].subject,
from: data.messages[i].from,
body: data.messages[i].body
});
vm.messaging.sent.messages.push(message);
}
},
error: function (jqXHR)
{
vm.error.handle(jqXHR.getResponseHeader("error"), jqXHR.status);
}
});
return true;
};
我使用Visual Studio“Web Essentials”扩展,允许您创建“捆绑”的js文件。 (选择js文件,右键单击它们并转到Web Essentials - &gt;创建Javascript捆绑文件)我的Bundle文件设置如下:
<?xml version="1.0" encoding="utf-8"?>
<bundle minify="true" runOnBuild="true">
<!--The order of the <file> elements determines the order of them when bundled.-->
<!-- Begin JS Bundling-->
<file>js/header.js</file>
<!-- Models -->
<!-- App1 -->
<file>js/models/app1/class1.js</file>
<file>js/models/app1/class2.js</file>
<!-- App2 -->
<file>js/models/app2/class1.js</file>
<file>js/models/app2/class2.js</file>
<!-- View Models -->
<file>js/viewModel.js</file>
<!-- Database -->
<file>js/database/app1.js</file>
<file>js/database/app2.js</file>
<!-- End JS Bundling -->
<file>js/footer.js</file>
</bundle>
header.js和footer.js只是文档就绪函数的包装器:
<强> header.js:强>
//put all views and view models in this
$(document).ready(function()
{
<强> footer.js:强>
//ends the jquery on document ready function
});
不要保留一个难以浏览的大怪异html文件。由于敲除和HTTP协议的无状态的绑定,你可以很容易地进入淘汰陷阱。但是,我使用两个选项进行分离,这取决于我是否将该作品视为是否被用户大量访问:
服务器端包括:(只是指向另一个html文件的指针。如果我觉得这个应用程序的一部分被用户大量使用,我想使用它,但我想将它保持分开)
<!-- Begin Messaging -->
<!--#include virtual="Content/messaging.html" -->
<!-- End Messaging -->
您不希望使用服务器端包含太多,否则用户每次访问页面时必须加载的HTML量将变得相当大。话虽如此,这是迄今为止最容易分离你的html的解决方案,同时保持你的淘汰赛绑定。
加载HTML内容async:(如果用户使用的特定部分不太频繁,我会使用此内容)
我使用jQuery加载函数来完成此任务:
// #messaging is a div that wraps all the html of the messaging section of the app
$('#messaging').load('Content/messaging.html', function ()
{
ko.applyBindings(vm, $(this)[0]); //grabs any ko bindings from that html page and applies it to our current view model
});
显示和隐藏你的knockout.js应用程序的不同部分很容易发疯,因为你需要设置这么多不同的on和off开关,这些代码很难管理和记忆。首先,我将每个页面或应用程序保存在自己的“div”中(并在其自己的html文件中进行分离)。示例HTML:
<!-- Begin App 1 -->
<div data-bind="visible: app1.visible()">
<!-- Main app functionality here (perhaps splash screen, load, or whatever -->
</div>
<div data-bind="visible: app1.section1.visible()">
<!-- A branch off of app1 -->
</div>
<div data-bind="visible: app1.section2.visible()">
<!-- Another branch off of app1 -->
</div>
<!-- End App 1 -->
<!-- Begin App 2 -->
<div data-bind="visible: app2.visible()">
<!-- Main app functionality here (perhaps splash screen, load, or whatever -->
</div>
<!-- End App 2 -->
其次,我会有一个类似于此的可见性功能,它可以为您网站上的所有内容设置可见性:(它还可以在子功能中处理我的导航)
vm.visibility:
{
set: function (page)
{
vm.app1.visible(page === "app1");
vm.app1.section1.visible(page === "app1section1");
vm.app1.section2.visible(page === "app1section2");
vm.app2.visible(page === "app2");
}
};
然后只需调用app或page的加载函数:
<button data-bind="click: app1.load">Load App 1</button>
其中有这个功能:
vm.visibility.set("app1");
这应该涵盖大型单页面应用程序的基础知识。可能有比我提出的解决方案更好的解决方案,但这并不是一个糟糕的方法。多个开发人员可以轻松地在应用程序的不同部分工作,而不会与版本控制发生冲突,而不会发生冲突。
答案 1 :(得分:7)
我喜欢使用原型继承来设置我的视图模型。像你一样,我有一个“主”视图模型。该视图模型包含其他视图模型的实例或视图模型的可观察数组,您可以在标记中使用“foreach”和“with”绑定。在“foreach”和“with”绑定中,您可以使用$ data,$ parent,$ parents和$ root绑定上下文来引用您的父视图模型。
以下是KO文档中的相关文章。
如果你想我可以把小提琴放在一起。让我知道。