Loading requirejs modules Dynamically with sugar s

2019-01-20 14:02发布

问题:

Hi i am trying to load some requireJs modules in a dynamic way by getting a list of filters and the iterate over the array to load this modules like this

define(function(require){
var runFilters = function(filters){
    var _ = require('underscore');
    var computedFilters  = getFilters(filters);
    var result = _.every(computedFilters,function(filter){
        return filter();
    });
    return result;
};

var getFilters = function(filters){
    var _ = require('underscore');
    return _.map(filters,function(filter){
        return require('filters/' + filter);
    },this);
}


var register = function(filters,fn){
    return function(){
        var args = Array.prototype.slice.apply(arguments);
        if(runFilters(filters))
            fn.apply(this,args);
    }
}
return{
    register: register
}
});

This give me error: Uncaught Error: Module name "filters/isAuth" has not been loaded yet for context: _

but when replacing this to a static way (just to test) it loads perfectly

define(function(require){
    var runFilters = function(computedFilters){
        var result = _.every(computedFilters,function(filter){
            return filter();
        });
        return result;
    };

    var getFilters = function(filters){
        var _ = require('underscore');
        return _.map(filters,function(filter){
            console.log(filter);
            return require('filters/' + filter);
        },this);
    }


    var register = function(filters,fn){
        var cachedFilters = [];
        cachedFilters.push(require('filters/isAuth'));
        return function(){
            var args = Array.prototype.slice.apply(arguments);
            if(runFilters(cachedFilters))
                fn.apply(this,args);
        }
    }
    return{
        register: register
    }
});

this give me the error too

cachedFilters.push(require('filters'+'/isAdmin'));

回答1:

You've run into a limitation of RequireJS's support for the CommonJS syntax. Here's the deal. When RequireJS defines a module, it looks at the factory function (the callback) you gave to define. It looks for this pattern:

/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g

This matches require calls like var foo = require('foo'); For each such call, it adds the required module to the list of dependencies of your module. So, for instance, something like:

// The `require` parameter must be present and is filled with something useful
// by RequireJS.
define(function (require) {
    var foo = require('foo');
    var bar = require('bar');
    ...
});

Is treated like this:

define(['require', 'foo', 'bar'], function (require) {
    var foo = require('foo');
    var bar = require('bar');
    ...
});

If you look carefully at the regular expression above, you'll see it will match only require calls that have a single parameter which is a literal string. So things like require("something" + somevar) won't work. RequireJS completely ignores them when doing this transformation.

The issue would not be fixable by changing the regular expression. RequireJS is at its heart a system for loading modules asynchronously. The form of the require call with a single string literal is sugar to allow porting more easily modules designed according to the CommonJS pattern, and for those who prefer this style (even if they are not porting anything). This type of call looks synchronous without actually being synchronous. To support cases where the name passed to it is computed, RequireJS would have to predict what the value would be.

If you want to have complete freedom to load whatever module you want in your code without knowing ahead of time what the name may be, then you have to use an asynchronous require call (require([computed_value], function(mod) {...})) which means your code must be asynchronous. If you have a limited set of modules you want to load and it is acceptable to always load them all, then you could require them all with literal strings:

define(function (require) {
    require("filters/isAdmin");
    require("filters/b");
    require("filters/c");
    require("filters/d");
    ...
});

RequireJS would do the transformation above and further require calls using computed values that resolve to one of the names you required early in your module would not fail.