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

Reply via email to