This document describes the challenges presented by import()
and how they
could be addressed.
If you're starting up an app with multiple modules already loaded, using
import()
prevents you from gaining access to them synchronously.
let loadFoo = () => import('./foo');
let loadBar = () => import('./bar');
From this code, you have no way of knowing if the ./foo
or ./bar
modules
are loaded already. The only option you have is to call loadFoo()
and
loadBar()
and wait for them to resolve.
Additionally, if either module has already been loaded, you have no way of gaining access to them synchronously.
This limits your ability to make decisions based on the state of the modules that are loaded.
In the browser, this is problematic because modules could take a much longer time to load based on the network performance. At the same time you need to make decisions on what to show the user.
In order to get around this, users are reaching into their build systems and depending on the implementation of how their modules are getting loaded.
For example, with Webpack:
let tryLoad = getWeakId => {
// Make sure we're inside of Webpack before we start doing things.
if (
typeof __webpack_require__ !== 'undefined' &&
typeof __webpack_modules__ !== 'undefined' &&
typeof require.resolveWeak !== 'undefined'
) {
let weakId = getWeakId();
// If you don't check this, and call __webpack_require__ it will cache an
// empty module
if (__webpack_modules__[weakId]) {
// __webpack_require__ could throw in some scenarios
try {
let obj = __webpack_require__(weakId);
// Make sure to interop if the Babel was possibly compiled with Babel.
return obj && obj.__esModule ? obj.default : obj
} catch (err) {}
}
}
return null;
};
// We need to put resolveWeak inside a function so we don't call it outside of
// a Webpack context. Also we need the exact expression
// `require.resolveWeak("...")` in order to work.
let foo = tryLoad(() => require.resolveWeak('./foo'));
async function main() {
// We need to repeat `./foo`
if (!foo) foo = await import('./foo');
foo();
}
- Add API (i.e.
System.moduleSpecifier
) to describe the module separately fromimport()
- Add API (i.e.
System.isLoaded(moduleSpecifier)
) to query if the module is loaded or not. - Add API (i.e.
System.getModule()
) to get access to loaded modules that we know are loaded.
const FOO_MODULE = System.moduleSpecifier('./foo');
const BAR_MODULE = System.moduleSpecifier('./foo');
async function main() {
let foo;
if (System.isLoaded(FOO_MODULE)) {
foo = System.getModule(FOO_MODULE);
} else {
document.body.innerHTML = 'Loading...';
foo = await import(FOO_MODULE);
}
foo();
}
Instead of querying if a module is loaded and then going to load it, instead
you could just try-catch
the call to System.getModule(FOO_MODULE)
which
would throw if the module is not already loaded.
const FOO_MODULE = System.moduleSpecifier('./foo');
const BAR_MODULE = System.moduleSpecifier('./foo');
async function main() {
let foo;
try {
foo = System.getModule(FOO_MODULE);
} catch (err) {
document.body.innerHTML = 'Loading...';
foo = await import(FOO_MODULE);
}
foo();
}
Before
let tryLoad = getWeakId => {
// Make sure we're inside of Webpack before we start doing things.
if (
typeof __webpack_require__ !== 'undefined' &&
typeof __webpack_modules__ !== 'undefined' &&
typeof require.resolveWeak !== 'undefined'
) {
let weakId = getWeakId();
// If you don't check this, and call __webpack_require__ it will cache an
// empty module
if (__webpack_modules__[weakId]) {
// __webpack_require__ could throw in some scenarios
try {
let obj = __webpack_require__(weakId);
// Make sure to interop if the Babel was possibly compiled with Babel.
return obj && obj.__esModule ? obj.default : obj
} catch (err) {}
}
}
return null;
};
// We need to put resolveWeak inside a function so we don't call it outside of
// a Webpack context. Also we need the exact expression
// `require.resolveWeak("...")` in order to work.
let foo = tryLoad(() => require.resolveWeak('./foo'));
async function main() {
// We need to repeat `./foo`
if (!foo) foo = await import('./foo');
foo();
}
After
// Create a module specifier in a static way that can be used by bundlers.
// We don't need to worry about the environment because we know this function
// will always exist (or at least be polyfilled).
let moduleSpecifier = System.moduleSpecifier('./foo');
let foo;
// Check if the module is already loaded.
if (System.isLoaded(moduleSpecifier)) {
// Get it synchronously.
foo = System.getLoadedModule(moduleSpecifier);
} else {
// Or wait for it to load
foo = await import(moduleSpecifier);
}
// Note we didn't need to repeat `./foo` multiple times.