ES6 async modules using multiple baseurls

2019-05-14 14:02发布

问题:

The ES6 module system seems to be a proper fit for unifying the CommonJs / AMD syntaxes. As a requireJs/AMD-user I'd like to convert to ES6 modules (using babel.js for now).

There seems to be one problem though; reading through the docs and tutorials, there doesn't seem to be possible to load module packages that are dependendent on more than one baseurl. Using requireJs this is solvable using the context field:

// async dependencies are loaded from http://path/to/domain
var contextedRequire1 = require.config({
  baseUrl: 'http://path/to/domain/js',
  context: 'mainContext'
});    

// async dependencies are located on http://path/to/otherdomain
var contextRequire2 = require.config({
  baseUrl: 'http://path/to/otherdomain/js',
  context: 'pluginContext'
});

contextedRequire1(['main.js'], function(main){
  // loaded using http://path/to/domain/js/main.js
  contextedRequire2(['plugin-lazyloading-deps.js'], function(plugin){
    plugin.init();
  });
});

In main.js

define(['main-deps'], function(mainDeps){
  // loaded using http://path/to/domain/js/main-deps.js
})

In plugin-lazyloading-deps.js

define(['require'], function(require){
  // loaded using http://path/to/otherdomain/js/plugin-lazyloading-deps.js
  if(Modernizr.touch) {
    require(['hammer'], function(){
      // loaded using http://path/to/otherdomain/js/hammer.js
      hammer.init();
    })
  }
})

In ES6 async module imports this isn't possible, since System is a singleton

System.baseURL = "http://path/to/domain/js";
System.import("main").then(function(main){
  // loaded using http://path/to/domain/js/main.js

  // This will potentially break when main.js tries to load hammer.js from http://path/to/domain/js
  System.baseURL = "http://path/to/otherdomain/js";
  System.import("plugin-lazyloading-deps").then(function(){ /** code **/ });
});

My question is: Is there something in the docs that I've missed (possible to subclass System to be able to config several baseUrls), or is this something in the works for future module extensions?

回答1:

At least with the current version of SystemJS, you can provide wildcard paths. https://github.com/systemjs/systemjs/wiki/Configuration-Options#paths-unstable

I haven't used it myself, but for your case, seems you'd do

System.baseURL = 'http://path/to/domain/js';
System.paths['plugin-*'] = 'http://path/to/otherdomain/js/plugin-*';


回答2:

It seems as if System.js has an (undocumented) way - by extending the System object using Object.create(System).

var context1 = Object.create(System);
context1.baseURL = 'http://path/to/otherdomain/js';
context1.import('plugin-lazyloading-deps').then(function(m){
  m.setSystem(context1);
  m.initialize();
));

Please note that until the System object is implemented in browsers/nodeJs, this approach may break. Hopefully though, the same effect may be achievable using class context1 extends System in ES6.

The implementation is not 100% analogous to requireJs, since it's not possible to inject the current context to async-load other modules from within a scoped context (ie. the 'require'-dependency needs to be replaced with m.setSystem(..)or similar).