【问题标题】:Backbone Marionette using Require.js, Regions and how to set upBackbone Marionette 使用 Require.js、Regions 以及如何设置
【发布时间】:2012-11-19 08:14:03
【问题描述】:

我目前正在编写一个 Backbone Marionette 应用程序,该应用程序最终包含大约 6 个不同的“屏幕”或页面,它们经常共享内容,我不确定如何最好地构建和访问区域。

我正在使用此处描述的应用程序/模块设置:StackOverflow question 11070408: How to define/use several routings using backbone and require.js。这将是一个应用程序,它会随着时间的推移添加新的功能和内容,并且需要可扩展(并且显然尽可能可重用)

我正在构建的单页应用程序在每个屏幕上都有 4 个主要部分:页眉、主要内容、次要内容、页脚。

所有页面的页脚将保持一致,其中 3 个页面的页眉将相同,并在其余 3 个页面上稍作修改(使用大约 80% 的相同元素/内容)。 “更多内容”区域将可在各个页面中重复使用。

在我的 app.js 文件中,我这样定义我的区域:

define(['views/LandingScreen', 'views/Header', 'router'], function(LandingScreen, Header, Router) {
    "use strict";
    var App = new Backbone.Marionette.Application();

    App.addRegions({
        header: '#mainHeader',
        maincontent: '#mainContent',
        morecontent: '#moreContent',
        footer: '#mainFooter'
    });

    App.addInitializer(function (options) {

    });

    App.on("initialize:after", function () {
        if (!Backbone.History.started) Backbone.history.start();
    });

    return App;
});

现在,回到上述帖子中的应用设置,处理区域的最佳方式是什么。我会在每个子应用程序中独立地重新声明每个区域吗?这似乎是保持模块尽可能独立的最佳方式。如果我走那条路,在子应用程序之间打开/关闭或隐藏/显示这些区域的最佳方式是什么?

或者,我是否保留 app.js 中声明的区域?如果是这样,那么我将如何最好地从子应用程序中更改和协调这些区域的事件?在 app.js 文件中定义区域似乎与将模块和核心应用程序相互了解的内容保持在最低限度有悖常理。另外,我看到的每个示例在主应用程序文件中都有 appRegions 方法。那么从子应用访问和更改这些区域的最佳做法是什么?

提前致谢!

【问题讨论】:

  • 抱歉耽搁了,根据我得到的两个答案,我一直在争论 AMD 与非 AMD。对于长期计划,尽管我觉得我花更多的时间来管理依赖项而不是我的应用程序的功能,但走 AMD 路线会更好。

标签: backbone.js marionette singlepage regions single-page-application


【解决方案1】:

我实际上有一个负责启动子应用程序的根应用程序,它通过它们应该显示的区域。我还使用了一个基于 Backbone.SubRoute 的自定义组件,它可以为子应用程序启用相对路由。

看看这个要点:https://gist.github.com/4185418

您可以轻松调整它以发送定义多个区域的 addRegions 的“配置”对象,而不是我发送给子应用程序的 start 方法的 region

请记住,每当您在 Marionette 中调用 someRegion.show(view) 时,它将首先关闭当前显示在其中的任何视图。如果您有两个不同的区域,每个区域都定义在自己的应用程序中,但它们都绑定到同一个 DOM 元素,那么唯一重要的是哪个区域最近调用了 show。但是,这很麻烦,因为您没有获得关闭前一个视图的优势 - 例如,取消绑定事件绑定器。

这就是为什么,如果我有一个子应用程序从某种根应用程序“继承”一个区域,我通常只是将实际区域实例从该根应用程序传递给子应用程序,并保存对该区域作为子应用程序的属性。这样我仍然可以调用subApp.regionName.show(view) 并且它运行良好 - 如果您试图将事件从您的区域冒泡到您的应用程序(因为该区域将属于根应用程序),那么唯一可能搞砸的就是您的事件链,而不是子应用程序)。我几乎总是使用Marionette.EventAggregator 的单独实例来管理事件,而不是依赖区域/视图/控制器/等的内置功能来解决这个问题。

也就是说,您可以两全其美 - 您可以将区域实例传递到您的子应用程序中,保存对它的引用以便您可以调用“关闭”,然后使用其 regionInstance.el 属性来定义您自己的区域实例指向相同的元素。

for(var reg in regions) if regions.hasOwnProperty(reg) {
    var regionManager = Marionette.Region.buildRegion(regions[reg].el,
            Marionette.Region);
    thisApp[reg] = regionManager;
}

这完全取决于您的优先事项。

【讨论】:

  • 同步的,很好的答案。我对你的要点有几个问题。 1) Application.root.js 的第 43 行:这个抽象会是什么样子? 2) 同样的第 73 行,initialize:ui 方法中的内容是什么?这足以与我做事的方式背道而驰,这部分让我感到厌烦。 3) 在 main.js 中,我假设从第 18 行开始的数组是我要向整个应用程序添加更多子应用程序的地方? 4)vent.super.js:第7行你说它应该只被根应用程序使用。所有其他应用程序都应使用“EchoVent”。什么是 EchoVent?它有什么特别之处还是标准的 EventAggregator?
  • 首先快速回答:3) 是的 4) EchoVent 是我用命名空间和至少一个现有的vent 实例作为参数实例化的supervent 的术语。它的初始化程序绑定到特殊的“all”事件,并简单地将其“回显”到任何已向 echovent 注册的通风口,并在事件名称前附加提供的命名空间。例如,如果我完成了var ev = new EchoVent("appName", rootVent),然后在 echoVent 上触发了“app:started”,它会按预期触发该事件,但也会在 rootVent 上触发appName:app:started
  • 1) 我刚刚为这个问题的答案创建了另一个要点,因为这里的 cmets 部分有点长。这是该链接:gist.github.com/4439430。哦,我忘了提到我在原始要点中添加了 echovent 代码,所以如果您想查看它,请阅读我原始答案中的要点。这是丑陋的,过度设计的代码,所以请不要评判我;P
  • 对不起,最后一个。 2)这主要是一种语义选择——我什至在我的实际应用程序中都没有该代码。它使用 Backbone.Wreqr 命令/执行模式,因为它是一种单点责任的事情——命令只有一个地方有处理程序,那就是在根应用程序的主控制器中。我们没有处理根路径的路由(长话短说),所以基本上当应用程序启动时,我只是发送一个命令来初始化 UI(显示默认视图),控制器会监听它。当它得到命令时,它会在主区域显示默认视图。
  • 所有这一切都清楚了,你太棒了。非常感谢!
【解决方案2】:

我个人更喜欢在我的 Marionette 应用程序中使用这些模块。我觉得它消除了 require.js 添加到您的应用程序的复杂性。在我目前正在开发的一个应用程序中,我创建了一个 app.js 文件来定义我的主干应用程序,但我正在使用一个控制器模块来加载我的路由、填充我的集合并填充我的区域。

app.js ->

var app = new Backbone.Marionette.Application();
app.addRegions({
   region1: "#region1",
   region2: "#region2",
   region3: "#region3",
   region4: "#region4"
});

app.mainapp.js ->

app.module('MainApp', function(MainApp, App, Backbone, Marionette, $, _) {
   // AppObjects is an object that holds a collection for each region, 
   // this makes it accessible to other parts of the application
   // by calling app.MainApp.AppObjects.CollectionName.... 
   MainApp.AppObjects = new App.AppObjects.Core();

   MainApp.Controller = new Backbone.Marionette.Controller.extend({
     start: function() {
       // place some code here you want to run when the controller starts
     } //, you can place other methods inside your controller
   });

   // This code is ran by Marionette when the modules are loaded
   MainApp.addInitializer(function() {
     var controller = new MainApp.Controller();
     controller.start();
   });
});

然后,您可以将您的路由放在另一个模块中,该模块将在控制器中访问。

然后在网页中,您将通过调用开始一切。

$(function () {
    app.start();
});  

Marionette 将自动运行并加载您的所有模块。

我希望这能让你朝着某个方向开始。抱歉,我无法复制并粘贴整个应用程序代码,以便为您提供更好的示例。这个项目完成后,我将重新创建一个可以推送到网络的演示应用程序。

【讨论】:

  • 卡尔珀斯,感谢您的意见。我确实很想走模块路线,但我最终认为它不会给我在路上所需的灵活性以确保我的应用程序的模块化。我为一个大型协会工作,需要创建尽可能可重用的模块。我知道从理论上讲,模块应该被编写成可重用的,但实际上不这样做太容易了。选择 AMD,虽然有时被证明是痛苦的,但这是我有点不情愿的选择。模块路线现在对我有帮助,但从长远来看会伤害我。非常感谢您的周到回复
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多