【问题标题】:Share resources across different amd modules在不同的 AMD 模块之间共享资源
【发布时间】:2012-01-12 21:01:13
【问题描述】:

我目前正在开发一个新的网络应用程序。

这是我第一次将 requirejs 与 AMD 模块一起使用。

要适应新范式并不容易——据我所知——全局命名空间中没有变量。

在以前的 Web 应用程序中,我总是在全局命名空间中有一个变量,我可以使用它来跨不同模块共享多个资源。

现在有了 requirejs AMD 模块,我使用了主干.js 和 jquery(两个 amd 版本 - jquery 1.7.1 和主干.js 0.5.3-optamd3)。

在我的应用程序的某个地方,我从服务器(用户对象)获取了一个主干.js 模块。我想从不同的 AMD 模块访问这个模块。我还想要一个应用程序范围的事件对象。

您能告诉我:在 requirejs AMD 中跨不同模块共享资源的正确方法是什么?

【问题讨论】:

    标签: backbone.js requirejs


    【解决方案1】:

    我自己找到了解决方案。

    感谢 IntoTheVoid 的回答,但我希望有类似 AMD 的解决方案。这意味着,不再是“污染”全局命名空间。

    我的解决方案有 2 个关键:

    https://github.com/addyosmani/backbone-aura”来自 Addy Osmani,“https://github.com/amdjs/amdjs-api/wiki/AMD”来自异步模块定义 (AMD) API 规范。

    规范说:“如果工厂是一个函数,它应该只执行一次。”

    因此,如果一个 amd 模块在 Web 应用程序中被多次指定为依赖项,则该依赖项不仅是 NOT LOADED MULTIPLE TIMES,它也是 NOT EXECUTED MULTIPLE TIMES strong>,这对我来说是新事物。它只执行一次,并保留工厂函数的返回值。具有相同路径的每个依赖项具有相同的对象。这改变了一切。

    因此,您只需定义以下 amd 模块:

    define([], function() {
        var app_registry = {};
        app_registry.global_event_obj = _.extend({}, Backbone.Events);
        app_registry.models = {};
        return app_registry;
    });
    

    现在,在您想要共享资源的那些模块中,您将这个 app_registry 模块声明为依赖项并写入一个:

    define(['jquery','underscore','backbone','app_registry'], 
      function ($, _, Backbone, app_registry){
        var firstView = Backbone.View.extend({
        initialize: function() {
            _.bindAll (this, 'methodOne'); 
            this.model.bind ('change', this.methodOne);   
            this.model.fetch();
        },  
        methodOne: function() {
            app_registry.models.abc = this.model;
        }
        ...
    

    在另一个方面:

    define(['jquery','underscore','backbone','app_registry'], 
      function ($, _, Backbone, app_registry){
        var secondView = Backbone.View.extend({
        initialize: function() {
           _.bindAll (this, 'methodTwo'); 
            app_registry.global_event_obj.bind ('special', this.methodTwo);
        },  
        methodTwo: function() {
            app_registry. ...
        }
        ...
    

    【讨论】:

    • 所以我可以将模型保存到这个全局对象并访问它的视图和子视图?
    • 那么这如何解决问题呢?假设第一个模型创建了models.abc,那么secondView如何在不担心模块加载顺序的情况下检索模型?
    • 这真的很有用。在我的应用程序中,我创建了一个模块,该模块以单例模式创建用于存储全局状态值的模型。我在顶级应用程序级别初始化它,并在那里侦听模型上的更新事件。然后我可以将它加载到需要读取或设置状态值的任何视图等中。
    【解决方案2】:

    要回答有关如何提供应用程序范围的事件对象的问题,您可以创建一个名为 globalContext 的 amd 模块并在 main.js 中实例化它。
    此后,您可以将设置附加到 globalContext 并使用全局上下文来创建子组件等。

        //main.js file
        require([ "./global-context" ], function( GlobalContext) {
                 new GlobalContext();
        });
    

    在 global-context.js 文件中,我们可以执行加载子模块等任务

        define(["Boiler", "./settings", "./modules/modules"], 
               function (Boiler, settings, modules) {
                      var GlobalContext = function () {
    
                    //here we use a template javascript class called Boiler.Context
                    var globalContext = new Boiler.Context("GlobalModule");
    
                    //add settings to the context which can be obtained globally throughout                    the application
                    globalContext.addSettings(settings);
    
                    //here we load the sub modules of the global context
                    globalContext.loadChildModules(modules);
    };
    

    这是我们在 BoilerplateJS 中实现的,这是一个用于大规模 javascript 产品开发的参考架构。

    【讨论】:

      【解决方案3】:

      您可以使用初始化模式将任何值注入到 requirejs 模块中(又名“依赖注入”)。

      在代码中的任何位置调用 require:

      var MyBackboneModule;
      
      MyBackboneModule = someWayToGetAReferenceToIt();
      
      require("module", function(Module) { 
        Module.initialize(MyBackboneModule);
      });
      

      将module.js文件定义为

      define([], function(){
         var BackboneModuleDoingThisAndThat;
      
         function initialize(pBackboneModule) {
            BackboneModuleDoingThisAndThat = pBackboneModule ;
         };
      
         function doSomething() {
            var x = BackboneModuleDoingThisAndThat.computeThis(42);
         };
      
      
         return { 
            initialize: initialize
         };
      });
      

      【讨论】:

        【解决方案4】:

        您可以在入口点 AMD 模块中定义一个全局 APPAPP 可以创建并持有对其他共享模块的引用:

        APP = {
            this.settings : function() {
                if(settingsModule == undefined){
                    //require, create and return it
                } else {
                    return settingsModule;
                }
            }
        }
        

        【讨论】:

        • 非常感谢您的回答。问题在于。当我只有自己的 Web 应用程序时,只有一个全局变量?可能吗?在 AMD 范式中是正确的。但是,如果我想创建一个可供其他人使用的框架呢?这个人有一个全局变量用于他自己的 Web 应用程序,另一个使用我的框架。
        • 你仍然可以有尽可能多的全局变量,这更像是一个关于干净代码的问题,不是吗?
        【解决方案5】:

        只是对您自己的答案的评论 - 您无需在 app_registry 模块中定义函数:

        define({
          global_event_obj: _.extend({}, Backbone.Events);
          models: {}
        });
        

        应该够了。

        见:the docs

        但要小心这些事情。在某些用例中,共享数据模块是有意义的。但是,如果您最终将其用作伪全局命名空间,那么它并不比仅使用全局变量好多少(但并不是说您正在这样做)。

        【讨论】:

          猜你喜欢
          • 2021-09-24
          • 2017-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-18
          • 2020-01-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多