仅在需要时动态注入模块

gus*_*nke 44 requirejs angularjs

我正在将Require.js与Angular.js结合使用.

某些控制器需要巨大的外部依赖性,而其他控制器则不需要,例如,FirstController需要Angular UI Codemirror.这是额外的135 kb,至少:

require([
  "angular",
  "angular.ui.codemirror" // requires codemirror itself
], function(angular) {
  angular.module("app", [ ..., "ui.codemirror" ]).controller("FirstController", [ ... ]);
});
Run Code Online (Sandbox Code Playgroud)

每次我的页面加载时我都不想要包含指令和Codemirror lib,只是为了让Angular满意.
这就是为什么我现在只在路线被击中时加载控制器,就像这里所做的那样.

但是,当我需要类似的东西时

define([
  "app",
  "angular.ui.codemirror"
], function(app) {
  // ui-codemirror directive MUST be available to the view of this controller as of now
  app.lazy.controller("FirstController", [
    "$scope",
    function($scope) {
      // ...
    }
  ]);
});
Run Code Online (Sandbox Code Playgroud)

如何告诉Angular ui.codemirror在app模块中注入模块(或任何其他模块)?
我不在乎它是否是一种完成此操作的hackish方式,除非它涉及修改外部依赖项的代码.

如果它有用:我正在运行Angular 1.2.0.

Nik*_*los 33

我一直试图将requirejs + Angular混合一段时间.到目前为止,我在Github上发布了一个小项目(angular-require-lazy),因为范围太大而不适合内联代码或小提琴.该项目表明了以下几点:

  • AngularJS模块是延迟加载的.
  • 指令也可以延迟加载.
  • 有一个"模块"发现和元数据机制(参见我的其他宠物项目:require-lazy)
  • 应用程序自动拆分为捆绑(即使用r.js工作构建)

怎么做:

  • 提供者(例如$controllerProvider,$compileProvider)由捕获config功能(技术我在第一次看到angularjs-requirejs-懒惰控制器).
  • 引导后,angular由我们自己的包装器替换,可以处理延迟加载的模块.
  • 捕获注射器并作为承诺提供.
  • AMD模块可以转换为Angular模块.

这个实现满足了你的需求:它可以延迟加载Angular模块(至少我正在使用的ng-grid),绝对是hackish :)并且不会修改外部库.

评论/意见非常受欢迎.


(编辑)这个解决方案与其他解决方案的区别在于它不进行动态require()调用,因此可以使用r.js(以及我的require-lazy项目)构建.除此之外,这些想法或多或少地在各种解决方案中趋同.

祝你们好运!

  • 嗨,我做了一些更新,包括用Karma测试.代码覆盖支持和详细文档即将推出! (2认同)

gus*_*nke 7

注意:使用Nikos Paraskevopoulos的解决方案,因为它更可靠(我正在使用它),并且有更多的例子.


好的,我终于通过这个答案的简短帮助找到了如何实现这一目标.

正如我在我的问题中所说,这已成为一种非常黑客的方式.它包含_invokeQueue在app模块的上下文中应用依赖模块的数组中的每个函数.

它是这样的(请在moduleExtender函数中加注):

define([ "angular" ], function( angular ) {
    // Returns a angular module, searching for its name, if it's a string
    function get( name ) {
        if ( typeof name === "string" ) {
            return angular.module( name );
        }

        return name;
    };

    var moduleExtender = function( sourceModule ) {
        var modules = Array.prototype.slice.call( arguments );

        // Take sourceModule out of the array
        modules.shift();

        // Parse the source module
        sourceModule = get( sourceModule );
        if ( !sourceModule._amdDecorated ) {
            throw new Error( "Can't extend a module which hasn't been decorated." );
        }

        // Merge all modules into the source module
        modules.forEach(function( module ) {
            module = get( module );
            module._invokeQueue.reverse().forEach(function( call ) {
                // call is in format [ provider, function, args ]
                var provider = sourceModule._lazyProviders[ call[ 0 ] ];

                // Same as for example $controllerProvider.register("Ctrl", function() { ... })
                provider && provider[ call[ 1 ] ].apply( provider, call[ 2 ] );
            });
        });
    };

    var moduleDecorator = function( module ) {
        module = get( module );
        module.extend = moduleExtender.bind( null, module );

        // Add config to decorate with lazy providers
        module.config([
            "$compileProvider",
            "$controllerProvider",
            "$filterProvider",
            "$provide",
            function( $compileProvider, $controllerProvider, $filterProvider, $provide ) {
                module._lazyProviders = {
                    $compileProvider: $compileProvider,
                    $controllerProvider: $controllerProvider,
                    $filterProvider: $filterProvider,
                    $provide: $provide
                };

                module.lazy = {
                    // ...controller, directive, etc, all functions to define something in angular are here, just like the project mentioned in the question
                };
                module._amdDecorated = true;
            }
        ]);
    };

    // Tadaaa, all done!
    return {
        decorate: moduleDecorator
    };
});
Run Code Online (Sandbox Code Playgroud)

完成此操作后,我只需要执行此操作:

app.extend( "ui.codemirror" ); // ui.codemirror module will now be available in my application
app.controller( "FirstController", [ ..., function() { });
Run Code Online (Sandbox Code Playgroud)

  • 使用Nikos的解决方案.我不再使用它了. (2认同)