【问题标题】:Does KnockoutJS provide suitable architecture for building large web apps?KnockoutJS 是否为构建大型 Web 应用程序提供合适的架构?
【发布时间】:2011-12-19 07:42:23
【问题描述】:

快速提问:

KnockoutJS 会为开发大型网络应用程序提供坚实的基础吗?我担心有一个巨大的 viewModel 会变得无法维护。

背景资料

我将构建一个基于客户端的网络应用程序。后端将只是一个 RESTful 端点。 Web 应用程序的整个界面将以纯 HTML/CSS/JS 构建 - 不涉及服务器端脚本。

网络应用程序本身将由几个较小的应用程序组成,只需一个常规登录名(有点像谷歌的网络应用程序,你有 Gmail、文档、日历、阅读器等)。

这些网络应用程序中的每一个都将具有一些通用功能(例如侧边栏树视图、顶部栏菜单视图、通知系统)和一些应用程序独有的功能。通常我会分解我的应用程序以封装功能,例如:

var myNamespace = {
    common: {
        settings: {},
        user: {},
        notifications: {}
    },
    app1: {},
    app2: {},
    app3: {}
};

现在,我真的很喜欢使用 KnockoutJS 并认为它在构建我的项目的某些元素时会很有帮助(例如通知系统,或者具有自动刷新功能的高级网格视图,因为该应用程序将支持协作)。但我只是不知道将我的 viewModel 放在这个结构中的哪个位置。

我只能找到关于如何使用 KnockoutJS 构建应用程序的简单示例。 你真的可以用它构建比 Twitter 阅读器更高级的东西吗?有没有很好的例子来说明如何将 viewModel 中的许多功能分解,或者分解成许多 viewModel?

建议的解决方案

虽然更理论的问题(快速问题)在这里仍然没有答案,但我想我已经找到了一个在实践中有效的解决方案。 @Simon 的回答让我深思,这就是我目前所得到的:

// First: a collection of Observables that I want to share
ld.collectionOfObservables = {
    notifications: ko.observableArray([]),
};

// Now let's define a viewModel. I put all my stuff inside the
// 'ld' namespace to avoid cluttering the global object. 
ld.viewModel1 = function (args) {
    // Look inside args and bind all given parameters 
    // Normally you will want args to be an object of Observables. 
    for (var key in args) {
        if (args.hasOwnProperty(key)) {
            this[key] = args[key];
        }
    };
    // So, by now we already have some observables in
    // 'this', if there were any supplied in 'args'.
    // Additionally, we define some model-unique properties/observables
    this.folders = [ 'Inbox', 'Archive', 'Sent', 'Spam' ];
    this.selectedFolder = ko.observable('Inbox');
};
// *** Let's pretend I create similar class and call it ld.viewModel2 ***
ld.viewModel2 = function (args) { .... }

// OK, now go on and instantiate our viewModels!
// This is the fun part: we can provide 0-many observables here, by providing them in an object
// This way we can share observables among viewModels by simply suppling the same observables to different viewModels
var vm1 = new ld.viewModel1({ 
    notifications: ld.collectionOfObservables.notifications,  // we take an Observable that was defined in the collection
});
var vm2 = new ld.viewModel2({ 
    notifications: ld.collectionOfObservables.notifications,  // shared with vm1
});

// Of course, we could just send the entire ld.collectionOfObservables as an array 
// but I wanted to show that you can be more flexible and chose what to share.
// Not easy to illustrate with *one* shared Observable - notifications - 
// but I hope you get the point. :)

// Finally, initiate the new viewModels in a specified scope
ko.applyBindings(vm1, document.getElementById('leftPane')); 
ko.applyBindings(vm2, document.getElementById('bottomPane'));

现在,如果 JS 有真正的继承,那就更好了,因为现在我觉得我所有的 viewModel 都以这个开头:

for (var key in args) {
    if (args.hasOwnProperty(key)) {
        this[key] = args[key];
    }
};

但这只是一个小小的不便。让我知道你的想法!

编辑 1: 解决方案可以像使用with: 绑定一样简单吗?示例见“1. Control flow bindings”。

编辑 2: 我认为我的最后一次编辑太快了。 with: 绑定可能有助于您的代码结构,但 AFAIK 它并不能帮助您在这些不同部分之间共享可观察值。所以上面提出的解决方案仍然是要走的路。

【问题讨论】:

    标签: architecture knockout.js


    【解决方案1】:

    我使用了局部视图(在 Nancy 而不是 MVC 中),每个视图都有自己的淘汰任务,每个视图模型都有自己的视图模型。我认为它工作得很好——一个复杂的页面被分成许多简单的独立部分。大多数部分视图都有自己的模块/控制器/端点,因此模块也很“瘦”。

    很遗憾 jQuery 模板被删除,但这是另一个问题。

    对不起,我刚刚重新阅读了您的帖子:没有服务器端的东西,所以没有办法分解页面?哎哟。我仍然认为很多视图模型是要走的路。

    【讨论】:

    • 是的,所以我应该多次调用 ko.applyBindings() 并提供可选的第二个参数(范围),对吗?但这会使例如变得更加困难。当另一个局部视图的视图模型(通知)中的某些内容发生更改时,一个局部视图(例如,网格)采取行动......
    • @Jacob - 您可以为此使用多个绑定,尽管这通常不是问题。多个绑定调用非常有用的地方是改善非常复杂页面的响应时间。我将绑定调用分为两部分。我首先绑定到页面上所有立即可见的元素。然后我绑定到所有不立即可见的元素。
    【解决方案2】:

    您可以使用局部视图并在它们之间共享可观察对象。

        var some_observable = ko.observable()
    
        var Model1 = function(something) {
            this.something_1 = something;
        };
        var Model2 = function(something) {
            this.something_2 = something;
        };
    
        var view_1 = Model1(some_observable);
        var view_2 = Model2(some_observable);
    
        ko.applyBindings(view_1, document.getElementById('some-id'));
        ko.applyBindings(view_2, document.getElementById('some-other-id'));
    
        <div id='some-id'>
            <input data-bind='value: something_1' />
        </div>
        <div id='some-other-id'>
            <input data-bind='value: something_2' />
        </div>
    

    我一直在使用这种方法在图库应用程序中维护照片列表,其中一个视图呈现缩略图,另一个视图负责上传。

    【讨论】:

    • 感谢您的想法。我将不得不对其进行试验,看看它是否可以很好地扩展。问题是我需要分享更多的可观察的,而不仅仅是一个,就像你的例子一样。所以我的模型构造函数必须采用 0:n 参数。也许它适用于var Model1 = function () {var args = arguments; // for each argument, assign it to this[argumentName]} 之类的东西。我得试试看!谢谢你的想法。
    • @Jacob - 你觉得这个锻炼怎么样?你最终成功地使用了这个例子吗?
    【解决方案3】:

    在我看来,我们可以使用 KO 并将视图模型共享到功能模块的范围内(比如具有多个 html 控件的功能小部件)。我们可以考虑使用TIBCO Page bus(Pub/Sub)在页面中的这些功能模块之间进行通信,以使功能模块保持页面解耦和可管理的方式。

    【讨论】:

      【解决方案4】:

      这是一篇旧帖子,但最近我在this repository that I call gcc-knockout 中构建了一个用于完全相同目的的框架。一切都是一个组件,甚至还有一个视图管理器可以完全切换视图并同时保留历史记录。我还没有正确记录它,但是 repo 附带了一个示例来演示它的一些功能。

      请注意,我还使用了 Google Closure Compiler。如果您正确导出将在 html 模板中使用的属性,您可以在高级模式下安全地使用它。组件使用 goog.events 进行通信,现在一切都很干净。我没有使用淘汰赛的订阅实用程序。随意检查并贡献!偶尔更新一下。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-07
        • 1970-01-01
        • 1970-01-01
        • 2015-06-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多