骨干网设计

发布于 2024-12-19 07:36:37 字数 985 浏览 0 评论 0 原文

我刚刚开始使用 Backbone。我浏览了前两个 PeepCode 截屏视频,非常棒,现在我正在研究未来应用程序的快速分离(无服务器端)模型。

这就是我想要构建的(大致)。一系列五个文本框 - 让我们称之为小部件。每个小部件输入在选择时将显示一个窗格,其中显示与小部件关联的任务,并允许用户创建新任务或销毁现有任务。

此时,我认为我有以下模型:

Widget
Task

以下集合:

Tasks
Widgets

以下视图(这就是它变得毛茸茸的地方!)

WidgetListView
  - Presents a collection of Widgets
WidgetView 
  - sub-view of WidgetListView to render a specific Widget
TaskPaneView 
  - Presented when the user selects a Widget input
TaskCreateView 
  - Ability to create a new Task associated with selected Widget
TaskListView 
  - Presents a collection of Tasks for the given widget
TaskView 
  - Displays Task detail - sub-view of TaskListView

假设这是合理的,技巧就变成了如何在选择 WidgetView 时显示 TaskPaneView。此外,TaskPaneView 应如何依次呈现 TaskCreateViews 和 TaskListViews。

这里真正的问题是:一个级联渲染事件是否跨视图?根视图是否允许知道子视图并显式渲染它们?这应该是事件驱动的吗?

如果这是一个开放式问题,我深表歉意,只是希望有人以前见过类似的东西并能够为我指明正确的方向。

谢谢!

I'm just getting started with Backbone. I went through the first two PeepCode screencasts which were great and now I'm digging in on a quick detached (no server side) mock-up of a future app.

Here's what I'm looking to build (roughly). A series of five text boxes - lets call these Widgets. Each Widget input, when selected, will display a pane that shows Tasks associated with the Widget and allow the user to create a new Task or destroy existing Tasks.

At this point, I'm thinking I have the following models:

Widget
Task

The following collections:

Tasks
Widgets

The following views (this is where it gets hairy!)

WidgetListView
  - Presents a collection of Widgets
WidgetView 
  - sub-view of WidgetListView to render a specific Widget
TaskPaneView 
  - Presented when the user selects a Widget input
TaskCreateView 
  - Ability to create a new Task associated with selected Widget
TaskListView 
  - Presents a collection of Tasks for the given widget
TaskView 
  - Displays Task detail - sub-view of TaskListView

Assuming that's reasonable, the trick becomes how to display a TaskPaneView when a WidgetView is selected. And futhermore, how that TaskPaneView should in turn render TaskCreateViews and TaskListViews.

The real question here is: Does one cascade render events across Views? Is it permissible for a Root view to know of sub-views and render them explicitly? Should this be event-driven?

Apologies if this is an open-ended question, just hoping someone will have seen something similar before and be able to point me in the right direction.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

多像笑话 2024-12-26 07:36:37

绝对使其成为事件驱动的。另外,尽量不要创建紧密耦合的视图。松耦合将使您的代码更易于维护且灵活。

查看这篇关于事件聚合器模型和主干的文章:

http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/

rel 版本是你可以这样做:

var vent = _.extend({}, Backbone.Events);

并使用 vent.trigger 和 vent.bind 来控制你的应用程序。

Definitely make it event driven. Also, try not to create views that are closely coupled. Loose coupling will make your code more maintainable as well as flexible.

Check out this post on the event aggregator model and backbone:

http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/

The short version is you can do this:

var vent = _.extend({}, Backbone.Events);

and use vent.trigger and vent.bind to control your app.

此生挚爱伱 2024-12-26 07:36:37

前言:我已经用下面编写的代码为您提供了要点:
https://gist.github.com/2863979

我同意 pub/sub (“观察者模式” )这是其他答案所建议的。不过,我也会使用 Require.js 和 Backbone 的强大功能。

阿迪·奥斯马尼 (Addy Osmani) 写了一些很棒的文章!有关 Javascript 设计模式和构建 Backbone 应用程序的资源:

http://addyosmani.com/resources/essentialjsdesignpatterns/book /
http://addyosmani.com/writing-modular-js/
http://addyosmani.github.com/backbone-fundamentals/

使用 AMD 的酷炫之处(在 Require.js 中实现)与 Backbone 一起解决了一些通常会遇到的问题。

通常,您将定义类并以某种命名空间的方式存储它们,例如:

MyApp.controllers.Tasks = Backbone.Controller.extend({})

这很好,只要您在一个文件中定义大部分内容,当您开始向混合中添加越来越多的不同文件时,它就会变得不那么健壮,您必须开始注意如何加载不同的文件,controllers\Tasks.js 之后 models\Task.js 等。您当然可以正确编译所有文件订单等,但还远非完美。

最重要的是,非 AMD 方式的问题是您必须将视图彼此更紧密地嵌套在一起。可以说:

MyApp.classes.views.TaskList = Backbone.View.extend({ 
    // do stuff
});

MyApp.views.App = Backbone.View.extend({
    el: '#app',
    initialize: function(){
        _.bindAll(this, 'render');
        this.task_list = new MyApp.classes.views.TaskList();    
    },

    render: function(){
        this.task_list.render();
    }
});

window.app = new MyApp.views.App();

一切都很好,但这可能会成为一场噩梦。

使用 AMD,您可以定义一个模块并给它一些依赖项,如果您对其工作原理感兴趣,请阅读上面的链接,但上面的示例将如下所示:

// file: views/TaskList.js
define([], function(){

    var TaskList = Backbone.View.extend({
        //do stuff
    });

    return new TaskList();
});

// file: views/App.js
define(['views/TaskList'], function(TaskListView){

    var App = Backbone.View.extend({
        el: '#app',

        initialize: function(){
            _.bindAll(this, 'render');
        },

        render: function(){
            TaskListView.render();
        }
    });

    return new App();
});

// called in index.html
Require(['views/App'], function(AppView){
    window.app = AppView;
});

请注意,在您返回实例的视图的情况下,我也对集合执行此操作,但对于模型,我会返回类:

// file: models/Task.js
define([], function(){

    var Task = Backbone.Model.extend({
        //do stuff
    });

    return Task;
});

一开始这可能看起来有点多,人们可能会认为“哇,这太过分了”。但是,当您必须在许多不同的模块(例如集合)中使用相同的对象时,真正的力量就会变得清晰:

// file: models/Task.js
define([], function(){

    var Task = Backbone.Model.extend({
        //do stuff
    });

    return Task;
});

// file: collections/Tasks.js
define(['models/Task'], function(TaskModel){

    var Tasks = Backbone.Collection.extend({
        model: TaskModel
    });

    return new Tasks();
});

// file: views/TaskList.js
define(['collections/Tasks'], function(Tasks){

    var TaskList = Backbone.View.extend({
        render: function(){
            _.each(Tasks.models, function(task, index){
                // do something with each task
            });
        }
    });

    return new TaskList();
});


// file: views/statistics.js
define(['collections/Tasks'], function(Tasks){

    var TaskStats = Backbone.View.extend({
        el: document.createElement('div'),

        // Note that you'd have this function in your collection normally (demo)
        getStats: function(){
            totals = {
                all: Tasks.models.length
                done: _.filter(Tasks, function(task){ return task.get('done'); });
            };

            return totals;
        },

        render: function(){
            var stats = this.getStats();

            // do something in a view with the stats.
        }
    });

    return new TaskStats();
});

请注意,“任务”对象在两个视图中完全相同,因此具有相同的模型、状态等。比必须在某一时刻创建 Tasks 集合的实例然后在整个应用程序中始终引用它要好得多。

至少对我来说,将 Require.js 与 Backbone 一起使用已经消除了在哪里实例化什么的巨大困惑。为此使用模块非常有帮助。我希望这也适用于你的问题。

ps 另请注意,您也可以将 Backbone、Underscore 和 jQuery 作为模块包含到您的应用程序中,尽管您不是必须这样做,但您可以使用普通的脚本标签加载它们。

Pre p.s.: I have made a gist for you with the code that I wrote below:
https://gist.github.com/2863979

I agree with the pub/sub ('Observer pattern') that is suggested by the other answers. I would however also use the power of Require.js along with Backbone.

Addy Osmani has written a few GREAT! resources about Javascript design patterns and about building Backbone applications:

http://addyosmani.com/resources/essentialjsdesignpatterns/book/
http://addyosmani.com/writing-modular-js/
http://addyosmani.github.com/backbone-fundamentals/

The cool thing about using AMD (implemented in Require.js) along with Backbone is that you solve a few problems that you'd normally have.

Normally you will define classes and store these in some sort of namespaced way, e.g.:

MyApp.controllers.Tasks = Backbone.Controller.extend({})

This is fine, as long as you define most things in one file, when you start adding more and more different files to the mix it gets less robust and you have to start paying attention to how you load in different files, controllers\Tasks.js after models\Task.js etc. You could of course compile all the files in proper order, etc, but it is far from perfect.

On top of this, the problem with the non AMD way is that you have to nest Views inside of each other more tightly. Lets say:

MyApp.classes.views.TaskList = Backbone.View.extend({ 
    // do stuff
});

MyApp.views.App = Backbone.View.extend({
    el: '#app',
    initialize: function(){
        _.bindAll(this, 'render');
        this.task_list = new MyApp.classes.views.TaskList();    
    },

    render: function(){
        this.task_list.render();
    }
});

window.app = new MyApp.views.App();

All good and well, but this can become a nightmare.

With AMD you can define a module and give it a few dependencies, if you are interested in how this works read the above links, but the above example would look like this:

// file: views/TaskList.js
define([], function(){

    var TaskList = Backbone.View.extend({
        //do stuff
    });

    return new TaskList();
});

// file: views/App.js
define(['views/TaskList'], function(TaskListView){

    var App = Backbone.View.extend({
        el: '#app',

        initialize: function(){
            _.bindAll(this, 'render');
        },

        render: function(){
            TaskListView.render();
        }
    });

    return new App();
});

// called in index.html
Require(['views/App'], function(AppView){
    window.app = AppView;
});

Notice that in the case of views you'd return instances, I do this for collections too, but for models I'd return classes:

// file: models/Task.js
define([], function(){

    var Task = Backbone.Model.extend({
        //do stuff
    });

    return Task;
});

This may seem a bit much at first, and people may think 'wow this is overkill'. But the true power becomes clear when you have to use the same objects in many different modules, for example collections:

// file: models/Task.js
define([], function(){

    var Task = Backbone.Model.extend({
        //do stuff
    });

    return Task;
});

// file: collections/Tasks.js
define(['models/Task'], function(TaskModel){

    var Tasks = Backbone.Collection.extend({
        model: TaskModel
    });

    return new Tasks();
});

// file: views/TaskList.js
define(['collections/Tasks'], function(Tasks){

    var TaskList = Backbone.View.extend({
        render: function(){
            _.each(Tasks.models, function(task, index){
                // do something with each task
            });
        }
    });

    return new TaskList();
});


// file: views/statistics.js
define(['collections/Tasks'], function(Tasks){

    var TaskStats = Backbone.View.extend({
        el: document.createElement('div'),

        // Note that you'd have this function in your collection normally (demo)
        getStats: function(){
            totals = {
                all: Tasks.models.length
                done: _.filter(Tasks, function(task){ return task.get('done'); });
            };

            return totals;
        },

        render: function(){
            var stats = this.getStats();

            // do something in a view with the stats.
        }
    });

    return new TaskStats();
});

Note that the 'Tasks' object is exactly the same in both views, so the same models, state, etc. This is a lot nicer than having to create instances of the Tasks collection at one point and then reference it through the whole application all the time.

At least for me using Require.js with Backbone has taken away a gigantic piece of the puzzling with where to instantiate what. Using modules for this is very very helpful. I hope this is applicable to your question as well.

p.s. please also note that you'd include Backbone, Underscore and jQuery as modules to your app too, although you don't have to, you can just load them in using the normal script tags.

余罪 2024-12-26 07:36:37

对于这种复杂性的东西,我可能建议使用 Backbone Aura,它还没有稳定的发行版本。 Aura 本质上允许您拥有多个完全独立的 Backbone 应用程序(称为“小部件”),在单个页面上运行,这可能有助于理清和平滑某些模型/视图逻辑。

For something with this kind of complexity, I might recommend using Backbone Aura, which has not yet had a stable release version. Aura essentially allows you to have multiple fully self-contained Backbone apps, called "widgets," running on a single page, which might help disentangle and smooth over some of your model/view logic.

悲念泪 2024-12-26 07:36:37

从经典的 MVC 角度来看,您的视图响应其关联模型的变化。

//initialize for view
initialize : function() {
    this.model.on("change", this.render(), this);
}

这里的想法是,每当视图的模型发生更改时,它就会自行渲染。

或者或另外,如果您更改视图上的某些内容,则可以触发控制器侦听的事件。如果控制器还创建了其他模型,它可以以某种有意义的方式修改它们,那么如果您正在侦听模型的更改,视图也会更改。

From a classical MVC perspective, your views respond to changes in their associated models.

//initialize for view
initialize : function() {
    this.model.on("change", this.render(), this);
}

The idea here is that anytime a view's model is changed, it'll render itself.

Alternatively or additionally, if you change something on a view, you can trigger an event that the controller listens to. If the controller also created the other models, it can modify them in some meaningful way, then if you're listening for changes to the models the views will change as well.

陪你到最终 2024-12-26 07:36:37

与克里斯·比斯卡迪的答案类似。这就是我所拥有的:

您创建一个全局 var Dispatcher(不必是全局的,只要可以从 Backbone 应用程序的范围访问即可):

Dispatcher = _.extend({}, Backbone.Events);

Dispatcher 将帮助您执行订阅的回调,其中事件不是由更改特别触发的在模型或集合中。很酷的是,Dispatcher 可以执行 Backbone 应用程序内部或外部的任何功能。

您可以在视图或应用程序的任何部分中使用bind()订阅事件:

Dispatcher.bind('editor_keypress', this.validate_summary);

然后在另一个视图或应用程序的部分中使用trigger()触发新事件:

Dispatcher.trigger('redactor_keypress');

使用调度程序的美妙之处在于它的简单性和订阅多个事件的能力同一事件的侦听器(例如,不同 Backbone 视图中的回调)。

Similar answer to Chris Biscardi's. Here's what I have:

You create a global var Dispatcher (doesn't have to be global as long as it can be accessed from the scope of Backbone app):

Dispatcher = _.extend({}, Backbone.Events);

Dispatcher will help you execute subscribed callbacks with events that are not particularly triggered by changes in models or collections. And cool thing is that Dispatcher can execute any function, inside or outside Backbone app.

You subscribe to events using bind() in a view or any part of the app:

Dispatcher.bind('editor_keypress', this.validate_summary);

Then in another view or part of the app you trigger new event using trigger():

Dispatcher.trigger('redactor_keypress');

The beauty of using a dispatcher is its simplicity and ability to subscribe multiple listeners (e.g. callbacks in different Backbone views) to the same event.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文