使用requirejs和backbonejs创建/访问全局对象

3
这是易于理解的形式呈现,所以请享受阅读 :)
我有一个主干应用程序,它通过main.js文件进行初始化,如下所示:
require([
    'backbone',
    'app',
    'models/session'
], function (Backbone, Application, SessionModel) {


    //new Application();
    window.App = {
      session: new SessionModel()
    };

    new Application();

    Backbone.history.start();
});

正如您所见,requirejs将'app'作为第二个参数进行了要求。

App只是一个路由器,看起来像这样(已删除不太重要的部分)

define(function(require){
    'use strict';

    var Backbone = require('backbone'),
        Header = require('views/header'),
        Login = require('views/login');

    var Router = Backbone.Router.extend({

        initialize: function(){
            this.header = new Header();
            this.header.render();
        },


        routes: {
            'login': 'showLogin'
        },

        showLogin: function() {
            this.showView(new Login(), {requiresAuth: false});
            this.header.model.set('title', 'Login');
        },

        showView: function(view, options) {

            if(this.currentView) this.currentView.remove();
            $('.content').html(view.render().$el);
            this.currentView = view;

            return view;
        }

    });

    return Router;
});

重要的是,在这里的第一行中,我正在要求。
Header = require('views/header');

头部视图在标准方式下需要另一个视图:

LogoutView = require('views/logout');

登出视图如下,这里我来到了问题的本质:
define(function(require){
    'use strict';

    var Backbone = require('backbone'),
        JST = require('templates')

    return Backbone.View.extend({
        model: App.session,

        template: JST['app/scripts/templates/logout.hbs'],

        events: {
            'submit form': 'logout'
        },

        initialize: function(){
            this.listenTo(this.model, 'change', this.render)
        },

        render: function(){
            this.$el.html(this.template(this.model.toJSON()));

            return this;
        },

        logout: function(e){
            //nothing important here
        }

    });
});

正如您在Backbone.View.extend之后的第一行所看到的,我正在尝试定义视图的model属性:

model: App.session,

我认为应该可以访问,因为我正在定义:

window.App = {
      session: new SessionModel()
    };

在main.js文件中。
但似乎存在一个require问题,因为require正在尝试获取程序的第一行中的所有文件:
require([
    'backbone',
    'app', <<--- in this line it's trying to get all the files required in the app.js 
    'models/session'
], function (Backbone, Application, SessionModel) {

然后我遇到了这个错误:

Uncaught ReferenceError: App is not defined   logout.js:8

这正是试图访问App全局变量的代码行:

 model: App.session,

这应该在运行main.js文件后定义,但是由于require正在获取文件并在LogoutView中找不到定义的App变量,因此它甚至无法进行。
我可以通过在视图的initialize函数内定义模型来解决这个问题,如下所示:
initialize: function(){
    this.model = App.session;
    this.listenTo(this.model, 'change', this.render)
},

但是我真正想要的是理解为什么会出现这个错误,以及使用Backbone.js和Require.js创建全局模型的最佳实践。
2个回答

1
definerequire中的依赖项列表(您require中的第一个参数或在其他模块中使用的简化包装器)告诉RequireJS在解释当前文件之前应加载和解释这些模块。
这里是发生的事情:
  1. 第一个requirebackboneappmodels/session作为依赖项
  2. 在评估其功能之前,它会加载其依赖项,然后寻找其依赖项
  3. 反复洗涤,直到达到views/logout
  4. 解释了views/logout,您尝试将App.session分配给view.model,但此时该对象不存在,并出现App is not defined错误

一种解决方案是创建一个 Session 对象的单例,并在需要时引用它。
例如,假设你有 globals/session
define(['models/session'], function (SessionModel) {
    return new SessionModel();
});

你会将你的应用程序定义为:


require([
    'backbone',
    'app',
    'globals/session'
], function (Backbone, Application, session) {

    //new Application();
    window.App = {
      session: session
    };

    new Application();

    Backbone.history.start();
});

"在您的view/logout中进行相同的更改。"
define(function(require){
    'use strict';

    var Backbone = require('backbone'),
        JST = require('templates')

    return Backbone.View.extend({
        model: require('globals/session'),
        ...
    });
});

是的,谢谢你的回答,我知道发生了什么,但感谢你列出要点,这肯定会帮助其他人解决问题。所以你的意思是,在 requirejs 范围之外没有全局变量的方法,你需要在其中附加它? - matewilk
你可以拥有全局变量,但你必须注意包含文件的顺序。我更喜欢有一个专门处理这些变量的模块,这样我就可以随意引用它们而不会污染全局作用域。 - nikoshr
是的,我的意思是在全局作用域中使用变量,而不会污染它,这是有点矛盾的陈述。但是,你是对的,最好不要污染全局作用域。谢谢你澄清这一点!哦,对了,还有一件事,在第一个代码片段中展示的是一个单例吗?它似乎每次需要时都返回一个新的SessionModel(NEW)。还是说这是一些requirejs的魔法? - matewilk
单例模式确实在第一个代码片段中,代码将仅在第一次 require 时被评估,无论发生在何处。 这是一种魔法,是的 :) - nikoshr

0
以下代码大致是我在发布这个问题之前尝试解决此问题的方式:
define(function(require) {
    'use strict';

    var SessionModel = require('models/session')

    var session = (function(){
        if(App.session instanceof SessionModel.constructor){
            return App.session;
        } else {
            App.session = new SessionModel()
            return App.session;
        }
    })();

    return session;
});

但我认为我因为应用程序未定义或者可能有更好的解决方案而放弃了它。

肮脏的技巧是我不得不创建:

window.App = {};

就在这之前,我正在运行整个应用程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接