【问题标题】:How to load bootstrapped models in Backbone.js while using AMD (require.js)使用 AMD (require.js) 时如何在 Backbone.js 中加载引导模型
【发布时间】:2012-03-28 21:40:28
【问题描述】:

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 数据的全局变量,然后在相关的初始化方法中使用该变量。

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

【问题讨论】:

  • 意识到这不是答案,转为评论:我使用 require.js 和 bootstrap。我基本上做你不想做的事情。第一个获取的页面具有我引导的 JSON 数据的全局变量。我复制/使用这些值来填充我的初始模型和集合。我不确定这是一种不好的方法。如果您担心污染全局空间,您不能在最初复制结果后删除变量吗?
  • “@”符号在做什么? “@accounts”和“@projects”代表什么?

标签: javascript backbone.js requirejs js-amd


【解决方案1】:

这就是我们以不污染全局命名空间的方式引导数据的方式。相反,它只使用 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);
  }

});

【讨论】:

  • 我还注意到 require.js 优化器需要 config.js 才能正常工作。请解释为什么您建议使用 globals.js 添加另一个层。
  • 我发现这可以工作,但由于模块是异步加载的,有时虚拟会在实际选项之前加载。我还发现在优化虚拟期权时会优先于实际期权。这是一个有趣的想法,但根据我的经验,这根本不起作用,我认为在其他答案中还有其他更好的方法可以实现这一目标。
  • 这是对我有用的设置,为了使它与优化器一起使用,我所做的是excludeShallow my config.js 这样它将始终使用在html
  • @dlrust 请使用 Asgaroth 推荐更新您的答案
  • 这可行,但请注意。确保您的数据中没有“”,否则它会被处理并以惊人的方式弄乱您的标签。
【解决方案2】:

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

通常需要将配置信息传递给模块。那 配置信息通常被称为应用程序的一部分,并且 需要有一种方法将其传递给模块。在 RequireJS 中, 这是通过 requirejs.config() 的配置选项完成的。模块 然后可以通过询问特殊的依赖“模块”来读取该信息 并调用 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>

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

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

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

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

【讨论】:

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

在 RequireJS 中,这是通过 requirejs.config() 的配置选项完成的。然后,模块可以通过请求特殊依赖“模块”并调用module.config() 来读取该信息。示例:

index.html

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

ma​​in.js

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

app.js

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

  return App;
});

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

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

【讨论】:

  • 有什么原因不能在 main.js 中执行 define 吗? main.js 文件在这里似乎有点不必要,除非 main 需要使用require 而不是define。换一种说法,你能不能把 app.js 里的所有代码都复制到 main.js 里,然后把配置改成config.main 而不是config.app?然后你可以一起删除 app.js 。在测试中,这似乎可行,但由于我是 requirejs 的新手,这可能是不好的做法。
  • 只要它是一个模块,它就可以检索module.config()[property/module name]中分配给它的数据。但是,通常main.js 保留用于通过require 语句将应用程序中的核心模块作为依赖项。
  • 我可能没有在上面的例子中使用最好的模块名称。而不是app.js 假装它是apis/GoogleAnalytics.js,因为它正在检索的数据是api_key。这将是main.js 需要的它自己的模块,而不是整个应用程序本身。在这种情况下,配置对象看起来像:require = { config: { 'apis/GoogleAnalytics' : { 'api_key': '0123456789-abc' } } };
【解决方案4】:

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

这是我的工作方式。与 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);
    //...
});

【讨论】:

  • @BraveDave 的回答对我有用,但这个没有用,虽然我希望这样做。
  • @shioyama,很有趣。你使用的是什么版本的requirejs?当你尝试这种方式时实际发生了什么?如果我们都得到不同的结果,似乎存在一些相当大的不一致。
  • 我无法获得@BraveDave 的解决方案,或者这个解决方案。现在我正在使用草率的全局变量方法。在我的模块中,module.config().myVar 始终为空。
  • 关于比赛条件的要点。我注意到了同样的问题。对我有用的解决方案是使用@OllieEdwards 方式 - excludeShallow: ["config"]
【解决方案5】:

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

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

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

【讨论】:

    【解决方案6】:

    我对 AMD 方法不是(太)熟悉,但你为什么不将 JSON 添加到 dom 中,而不是使用全局变量。

    例如:

    var json = ...,
    $jsonContainer = $(json).wrap("<script id='json-container' type='text/javascript'>").appendTo($("body"));
    

    然后,代替骨干文档建议的嵌入脚本标签,在文档内部准备好:

    $(function(){
        MyCollection.reset($("#json-container").html());
        ...
    });
    

    【讨论】:

    • 虽然这会奏效(根据最初的问题,这是一个全球性的问题),但它与 AMD 的理念背道而驰。 AMD 模块应该声明其所有依赖项,最好不要依赖全局变量,“文档”当然是(由 $ 用于查找 HTML)。
    【解决方案7】:

    做这样的事情怎么样:

    <script>
    define('Models', ['backbone'], function(Backbone) {
        var Models = {
            Accounts: new Backbone.Collection,
            Projects: new Backbone.Collection
        };
    
        Models.Accounts.reset(<%= @accounts.to_json %>);
        Models.Projects.reset(<%= @projects.to_json(:collaborators => true) %>);
    
        return Models;
    });
    </script>
    

    然后你就可以像这样在其他模块中使用模型了:

    var models = require(['Models']);
    models.Accounts.doWhatYouNeed();
    

    或者这个:

    define(['any', 'dependencies', 'and', 'Models'], function(a, b, c, Models) {
       // Models will be available here
    });
    

    【讨论】:

    • @opengrid - 这就是为什么它在
    • 我喜欢这种对@dlrust 回答的倒置。虽然,它依赖于您的视图和集合能够处理可能已经呈现后“重置”的任何数据,虽然这通常是一个好主意,但可能涉及大量额外的簿记。
    • 不是每次调用require(['Models']); 时都会实例化集合吗?
    【解决方案8】:

    如上所述,“数据模块”(或配置,或任何您想调用的名称)可以包含在已经生成的文件中(例如 index.html),但我认为这相当难看。

    另一种方法是在其自己的模块文件中声明它,但这需要在生产环境中与服务器进行额外的往返。一旦你想构建和优化你的 requirejs 依赖项,数据模块就不能被包含,因为它是在页面加载时动态生成的。

    第三种选择可能是将其附加到您提供的文件之一(例如,优化的 requirejs 文件),但我不知道如何/是否可以这样做。

    【讨论】:

      【解决方案9】:

      @dlrust 的 Ansewr 工作,但它无法扩展参数并从代码中的一个位置传递多个。如果你尝试在你的渲染模板中做这样的事情:

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

      在另一个文件中添加一些数据

      <script>
          define('config', function() {
              return {
                  goods: <%= some data %>,
                  showcaseList: <%= some json or array %>
              };
          });
      </script>
      

      它被覆盖了(不扩展!!!)。在配置中将只有最后声明的数据。

      我的解决方案:使用带有设置/获取数据的 Backbone 模型。

      app.js

      define("App", [], function() {
          window.App = {
              // модели
              Model: {},
              // коллекции
              Collection: {},
              // виды
              View: {},
              // роутеры
              Router: {},
              // модальные окна
              Modal: {},
              // UI компоненты
              UI: {}
          };
          return window.App;
      });
      

      global.js

      define(["App", "underscore", "backbone"], function(App, _, Backbone) {
          "use strict";
      
          // модель глобальных данных
          App.Model.Global = Backbone.Model.extend({
              defaults: {}
          });
      
          return new App.Model.Global;    
      });
      

      index.php

      <!DOCTYPE html>
      <html>
          <head>
              <!--HEAD_START-->
              <script type="text/javascript" data-main="/app/init" src="/app/require/require.js"></script>
              <!--HEAD_END-->
          </head>
      
          <body>          
              <div id="tm-inner-wrap">
                  <div id="loader"><i class="uk-icon-refresh uk-icon-spin"></i></div>
                  <!--HEADER_START-->
                  <?= $this->includeTpl('header_view'); ?>
                  <!--HEADER_END-->
      
                  <!--CONTENT_START-->
                  <div>your html content data</div>
                  <!--CONTENT_END-->
      
                  <!--FOOTER_START-->
                  <?= $this->includeTpl('footer_view');?>
                  <!--FOOTER_END-->
      
                  <script>
                      require(["global"], function(Global) {
                          Global.set("notifyList", <?=json_encode($this->notifyList);?>);
                      });
                  </script>
              </div>
          </body>
      </html>
      

      另一个模板

      someTemplate.php

      <div class="tm-inner-body">
          <div class="uk-container uk-container-center">
              // content data
          </div>
      </div>
      
      <script>    
          require(["global", "module/index"], function(Global) {
              Global.set("goodList", <?=json_encode($this->goodList);?>);
          });
      </script>
      

      index.js

      require(["App", "core", "jquery", "uikit!uikit-addons-min", "underscore", "backbone", "global", "module/good/goodView"], function(App, Core, $, UIkit, _, Backbone, Global, goodView) {
          "use strict";
      
          // Global.get("notifyList"); its too able
      
          App.Collection.Good = new Backbone.Collection(Global.get("showcaseList")["items"]);
      
          // вид списка товаров
          App.View.GoodList = Backbone.View.extend({
              // елемент
              el: ".tm-good-list",
              // init
              initialize: function() {
                  this.collection = App.Collection.Good;
                  // список товаров
                  this.drawList();
              },
              // отрисовка списка
              drawList: function() {
                  this.$el.empty();
                  this.collection.each(function(item, index) {
                      this.$el.append(this.drawItem(item));
                  }, this);
              },
              // отрисовка елемента
              drawItem: function(data) {
                  var good = new goodView({model: data});
                  return good.render().el;
              }
          });
      
          App.View.Index = Backbone.View.extend({
              el: "body",
              // пользовательские события
              events: {
                  //
              },
              // init
              initialize: function() {
                  var $this = this;
                  if(Global.get("showcaseList")) new App.View.GoodList();
              }
          });
      
          new App.View.Index();
      });
      

      文件结构:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-20
        • 2012-03-09
        • 1970-01-01
        • 2011-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多