for the forseeable future, the only practical solution i see to these frontend performance issues is to avoid using es modules, and revert to the simple, old-fashioned method of assigning libraries to the window namespace.
On 8/20/17, James Browning <thejameserna...@gmail.com> wrote: > These are just some ideas I've had for improving es modules based on > my experiences with them. The syntax and stuff with them isn't too > important, the main point is the problem I'm trying to solve with each > of them, feedback and criticism is welcome. > > # Dynamic modules > > One of the biggest issues I've had with ES modules is not being able > to load classic scripts as part of the dependency graph, one of the > solutions I've used but am not particularly happy with is having an > async loader function e.g.: > > ```js > function loadScript(url) { > return new Promise(resolve => { > const scriptElem = document.create('script') > scriptElem.src = url > scriptElem.onload = resolve > }) > } > > // some other file > > async function computeSpline() { > await loadScript('./mathjs.js') > // use math here > } > ``` > > And while this approach works somewhat it's a bit of a pain for a > couple reasons: > > - If something gains a `script` dependency it necessarily breaks all > consumers by becoming asynchronous even if the original operations > were synchronous > - It's not generic for other types of resources e.g. I can't load an > image without creating another loader function or so on > > --- > > My proposed solution, dynamic (but static) export: > > ```js > // math.mjs > import loadScript from ".../loadScript.js" > > loadScript('./math.js').then(_ => { > const math = window.math > delete window.math > export({ > math as default > }) > }) > ``` > > This solution is also generic so it can be used for loading any type > of resource: > > ``` > // highPerformanceMath.mjs > fetch('.../math.wasm').then(response => response.arrayBuffer()) > .then(buffer => WebAssembly.instantiate(buffer, {})) > .then(({ instance }) => { > export({ > instance.exports as default > }) > // or potentially named exports > export({ > instance.exports.fastFourierTransform as fastFourierTransform > ... > }) > ``` > > Now this solution would be nice because it's generic and allows for > loading any (even asynchronous) object as part of the module graph and > doesn't cause explosions where because one part becomes asynchronous > everything becomes asynchronous. > > However there is a deficiency in that it can be quite verbose for > similar tasks e.g. loading WebAssembly modules which is why I thought > of idea 2: > > # Module Arguments > > Effectively module arguments would allow passing data to a module > (statically) during loading e.g.: > > ```js > // some-file.js > import dict from "./dictionary.js" where { lang = "en-US" } > > // dictionary.js > fetch(`./dictionaries/${ import.arguments.lang }.txt`) > .then(response => response.text()) > .then(text => export({ > JSON.parse(text) as default > }) > ``` > > This solves the previous problem of very similar dynamic modules for > similar types by allowing details like that to be passed in as > arguments e.g.: > > > ```js > import math from "./loadScript.mjs" where { > script = './math.js', > globalName = 'math' > } > ``` > > # Lazy Export-From > > One of the nice things about named exports is you can minimize the > amount of mostly similar `import` declarations e.g.: > > ```js > import map from "./lodash/map.js" > import filter from "./lodash/filter.js" > import flatMap from "./lodash/flatMap.js" > ... > > // can become > import { map, filter, flatMap, ... } from "./lodash/lodash.js" > ``` > > However it has a major downside of massively increasing the amount of > fetch/parse/execute time for all those additional things exported by > the combined module. > > My idea is to allow modules to declare that parts need to not be > fetched parsed or executed if they're not actually imported e.g.: > > ```js > // my-operators-library.js > static export { map } from "./map.js" > static export { filter } from "./filter.js" > static export { reduce } from "./reduce.js" > ``` > > Effectively all my idea adds is the `static export` (syntax not > important) form that effectively says these names should only be > resolved if they're actually imported and can be safely ignored if > they're not used. This way you get both the benefits of collection > modules (easier to `import` and reduces duplication) and the benefits > of individual `import`s (lesser loading sizes). > > # Summary > > Basically the ideas suggested here are to solve these particular > problems I've had with ES modules: > > - Unable to load classic scripts (and other types of resources > statically e.g. conditional modules) as part of the module graph > - Unable to specify more specific behavior for a module to prevent > duplication > - Either have to have lots of almost duplicate import declarations or > have to load unnecessary files > > The solutions I proposed aimed to keep the constraint that module > exports should remain statically parsable which is why `export({ ... > })` shares the syntactic form. > > I refrained from specifying the semantics of the specific operations > as there's details that'd need to be sorted out for all of them if > there is any interest whatsoever in implementing them. > _______________________________________________ > es-discuss mailing list > es-discuss@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > _______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss