使用 AMD (require.js) 时如何在 Backbone.js 中加载引导模型

IT技术 javascript backbone.js requirejs js-amd
2021-02-13 14:11:51

Backbone.js 文档建议以这种方式加载引导模型:

<script>
var Accounts = new Backbone.Collection;
Accounts.reset(<%= @accounts.to_json %>);
var Projects = new Backbone.Collection;
Projects.reset(<%= @projects.to_json(:collaborators => true) %>);
</script>

但这是一种不能在AMD 方法中使用的模式(使用 require.js)

唯一可能的解决方案是声明存储 JSON 数据的全局变量,然后在相关的初始化方法中使用此变量。

有没有更好的方法来做到这一点(没有全局变量)?

6个回答

这就是我们以不污染全局命名空间的方式引导数据的方式。相反,它只使用 require.js。它还可以帮助您根据模板中的变量提供初始应用程序配置。

在您呈现的页面中

<script src="require.js"></script>
<script>
define('config', function() {
  return {
    bootstrappedAccounts: <%= @accounts.to_json %>,
    bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
  };
});
</script>
<script src="app.js"></script>

globals.js

此文件检查配置并使用返回的任何数据扩展自身

define([
  'config',
  'underscore'
], function(config) {

  var globals = {
  };
  _.extend(globals, config);
  return globals;

});

config.js

如果您希望能够加载应用程序,而不管您是否config在页面中定义则需要此文件

define(function() {
  // empty array for cases where `config` is not defined in-page
  return {};
});

app.js

require([
  'globals',
  'underscore',
  'backbone'
], function(globals) {

  if (globals.bootstrappedAccounts) {
    var accounts = new Backbone.Collection(globals.bootstrappedAccounts);
  }
  if (globals.bootstrappedProjects) {
    var projects = new Backbone.Collection(globals.bootstrappedProjects);
  }

});
@dlrust 请使用 Asgaroth 推荐更新您的答案
2021-03-15 14:11:51
我还注意到 require.js 优化器需要 config.js 才能正常工作。请解释为什么您建议使用 globals.js 添加另一个层。
2021-03-16 14:11:51
我发现这可以工作,但因为module是异步加载的,有时虚拟选项会在实际选项之前加载。我还发现在优化虚拟选项时,虚拟选项会比真实选项更受欢迎。这是一个有趣的想法,但根据我的经验,这根本不起作用,我认为在其他答案中还有其他更好的方法可以实现这一目标。
2021-03-19 14:11:51
这有效,但要小心。确保您的数据中没有“</script>”,否则它会被处理并以惊人的方式弄乱您的标签。
2021-03-22 14:11:51
这是对我excludeShallow有用的设置,为了使它与优化器一起工作,我所做的是对我的 config.js 这样它将始终使用 html 中定义的module
2021-04-12 14:11:51

看起来您可以使用 require.config() 函数或带有“config”选项的“require”全局,以便通过特殊依赖项“module”将数据传递给module。请参阅http://requirejs.org/docs/api.html#config-moduleconfig

通常需要将配置信息传递给module。该配置信息通常被称为应用程序的一部分,需要有一种方法将其传递给module。在 RequireJS 中,这是通过 requirejs.config() 的配置选项完成的。然后module可以通过请求特殊的依赖“module”并调用 module.config() 来读取该信息。

因此,对于我们拥有的引导模型,在顶级 HTML 页面中:

<script>
var require = {
    config: {
        'app': {
            bootstrappedAccounts: <%= @accounts.to_json %>
            bootstrappedProjects: <%= @projects.to_json(:collaborators => true) %>
        }
    }
};
</script>
<script src="scripts/require.js"></script>

然后在应用module(app.js)中,我们有:

define(['module'], function (module) {
    var accounts = new Backbone.Collection( module.config().bootstrappedAccounts );
    var bootstrappedProjects = new Backbone.Collection( module.config().bootstrappedProjects );
});

这里的“module”是为这些类型的情况提供的特殊依赖项。

这是未经测试的,但从文档中看起来很确定。

很好的答案。请注意,确实必须使用define()(如答案所示)而不是require()为了使 module.config() 工作。
2021-03-26 14:11:51
我希望我早点找到你的解决方案!只花了三个小时,得出了同样的结论。正准备将其发布为问答并找到您的回复。:-)
2021-03-30 14:11:51
不错的解决方案,对我有用,并且还应该与优化器一起使用(还没有那么远)。我注意到'app'对应于加载对象的module,所以如果你想在另一个module(例如otherModule.js)中使用引导数据,它需要在config : { 'otherModule': otherModuleBootstrapData }
2021-04-10 14:11:51

在 RequireJS 中,这是通过requirejs.config(). 然后module可以通过请求特殊依赖项“module”并调用module.config(). 例子:

索引.html

<script>
  var require = {
    config: {
      'app': {
        'api_key': '0123456789-abc'
      }
    }
  };
</script>
<script src="js/libs/require.js" data-main="js/main"></script>

主文件

require( ['app'], function(App) {
  new App();
});

应用程序.js

define( ['module'], function(module) {
  var App = function() {
    console.log( 'API Key:', module.config().api_key );
  };

  return App;
});

请注意,配置对象的名称必须与module的名称匹配在我的示例中,module的名称是app,因此还需要命名配置对象的名称app在module中,您需要包含['module']作为依赖项并调用module.config()[property name]以检索配置数据。

阅读有关此的文档:http : //requirejs.org/docs/api.html#config-moduleconfig

只要它是一个module,它就能够在module.config()[property/module name]. 但是,通常main.js保留用于通过require语句要求应用程序中的核心module作为依赖项
2021-03-21 14:11:51
是否有理由不能define在 main.js 中执行 a 这里的 main.js 文件似乎有点不必要,除非 main 需要使用require而不是define. 换句话说,你能不能把 app.js 中的所有代码都复制到 main.js 中,然后将配置更改为config.main而不是config.app然后你可以一起删除 app.js 。在测试中,这似乎有效,但由于我是 requirejs 的新手,这可能是不好的做法。
2021-03-28 14:11:51
我可能没有在上面的例子中使用最好的module名称。而不是app.js假装它是apis/GoogleAnalytics.js因为它检索的数据是api_key. 这将是 需要的它自己的module,而main.js不是整个应用程序本身。在这种情况下,配置对象将如下所示:require = { config: { 'apis/GoogleAnalytics' : { 'api_key': '0123456789-abc' } } };
2021-04-14 14:11:51

这里的一些答案让我接近了我的类似问题,但没有解决问题。特别是给出的排名最高且被接受的答案似乎给了我一个令人讨厌的竞争条件,有时虚拟对象会首先加载。当与优化器一起使用时,这也 100% 发生。它还为module使用显式字符串名称,require 文档特别建议您不要这样做。

这是我的工作方式。与 Brave Dave 类似,我使用 config 对象来捕获参数(在我的例子中来自 jsp 页面),如下所示

<script type="text/javascript">
    var require = {
        config: {
            options : { 
                bootstrappedModels : ${models}
            }
        }
    }
</script>

特别注意,我的参数位于一个名为 options 的对象中。这个名字不是可选的!虽然文档没有提到这一点,但以下是 require 将如何加载您的配置(requirejs 2.1.1 中的第 564 行):

config: function () {
    return (config.config && config.config[mod.map.id]) || {};
},

关键点是配置对象上必须有属性,其键 mod.map.id 解析为“选项”。

从这里您现在可以像这样访问模型

define(['module'], function(module){
    console.log(module.config().bootstrappedModels);
    //...
});
关于比赛条件的好点。我注意到了同样的问题。对我有用的解决方案是使用@OllieEdwards 方式 - excludeShallow: ["config"]
2021-03-23 14:11:51
我一直无法获得@BraveDave 的解决方案,或者这个解决方案起作用。现在我正在使用草率的全局变量方法。在我的module中,module.config().myVar始终为空。
2021-03-31 14:11:51
@shioyama,有趣。你用的是什么版本的requirejs?当您尝试这种方式时,实际发生了什么?如果我们都得到不同的结果,似乎存在一些相当大的不一致。
2021-04-05 14:11:51
@BraveDave 的答案对我有用,但这个答案不适用,尽管我希望它可以。
2021-04-09 14:11:51

您可以在 AMD module的末尾添加一个循环函数来检查何时定义了 init 方法(以便它可以在主体之后填充,或从包含加载)。这样module就可以保证可用,并且可以在准备好时进行初始化。

require(...,function (...) {
   //define models collections, etc..

   var initme = function () {
     if(document.initThisModule) 
       document.initThisModule();
     else
       setTimeout(initme, 10);
   }();
});