-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Start using JavaScript Modules and Import Maps #36716
Comments
We discussed the same topic during the bi-weekly WordPress Core JavaScript office hours (link requires registration at https://make.wordpress.org/chat/): https://wordpress.slack.com/archives/C5UNMSU4R/p1637075934082700 A short summary:
|
Maybe we don't need to drop bundling, and just import the bundle as a module, and let it export individual packages which could be then consumed as modules. I believe import maps could make it a bit easier. {
"imports": {
"bundle": "/bundle.js",
"@wordpress/components": "/* inlined module that does something like `import { package } from "bundle"; export { ...package };*/"
}
} See https://stackblitz.com/edit/import-map-to-bundled-esm-package?file=index.html for a POC / more code-like explanation of the idea. (Chromium / import-maps env only) |
For me the biggest performance improvement here is not something we can measure 1-1 by switching to ESM packages but it's more related to us being smarter when loading things, some examples:
On the opposite side of the spectrum, making the "components" package for instance ESM won't have any impact on performance as it's probably a requirement on most JS UI pages so it's always loaded synchronously. |
I've looked into how modules are implemented in various browsers, and they follow the spec in this, with the use of the same origin policy. Loading third party scripts will not be a problem, as long as they're served from the origin, but any module scripts served from a CDN or third-party provider would need the appropriate CORS headers in place, which is a new restriction when compared to classic scripts. This means that things like self-hosted module-based plugins and themes would not be an issue most of the time, unless they load scripts from external sources for some reason. There is no workaround for this new restriction, as the behaviour of classic scripts is considered a flaw from the security point of view, and while the behaviour of classic scripts can't be changed because of legacy reasons, the flaw has been corrected in new features like modules. There is an interesting discussion on this topic in the whatwg html repo, for anyone who'd like more details. |
I don't think we should try to unbundle, or attempt extreme levels of modularisation. At this point, I believe the right tradeoff is to provide and encourage the same level of bundling as we currently do, but expose these bundles as ESM for native browser loading. If anyone's interested in some outdated explorations into unbundling JS through ESM, I ran benchmarks and wrote an article on the topic a few years ago. TL;DR: even with HTTP/2, there are persistent bottlenecks to this approach. The main benefit to going all-in on ESM isn't increased modularity, as I see it, but rather better performance defaults. Classic scripts are blocking by default, and that's how By contrast, module scripts use the Furthermore, a well-designed ESM API would still be able to maintain all the performance benefits of the current
With all of this in mind, a well-designed ESM API would get us much closer to good performance defaults for scripts, as well as an easy way to do the right thing where it comes to lazy loading. And anyone that's worked in designing or supporting APIs surely knows the importance of good defaults, since that's what most developers end up going for 🙂 |
@sgomes, thank you for doing the research and sharing your findings.
100% agree with that. I filed the issue #2768 to discuss options for lazy loading blocks (or their parts) back in 2017! We haven't been able to come up with any solution that would work seamlessly for WordPress core, not mentioning 3rd party blocks. There was some progress with the Block Directory that lets users install blocks on the fly, but it still doesn't use a proper lazy loading of JavaScript but instead injects all necessary scripts with a clever workaround: gutenberg/packages/block-directory/src/store/load-assets.js Lines 53 to 81 in 50b19e7
There was also a promising exploration #21244 started by @spacedmonkey to expose all the registered scripts with the new REST API endpoint. However, it didn't materialize because it uncovered the complexity of the existing system built on top of the I think i18n support might still be a challenge when dealing with lazy loading, even in the ESM world. However, it feels like moving the management of all complex dependencies to the client would resolve many of the limitations the
Those are great examples shared by @youknowriad that would immediately contribute to the better performance of the block editor. We could use also lazy load the metaboxes as proposed in #32665. The same applies to the frontend. In #36176 @aristath explores adding handling for multiple scripts defined for the single block using the Navigation core block. With good support for lazy loading, we could move the complex logic from PHP to JavaScript and decide when a given chunk needs to be loaded. |
I'm betting this is beyond the scope, but I would hope any API solution includes some sort of version management. Any performance gains we get from ESM are barely make a dent when plugins are all multiple duplicates of the same libraries. |
Thank you, @justlevine, that is definitely a good point that hadn't yet been discussed as part of this thread! Some consideration needs to be given to versioning, and how a situation like the one you describe could be avoided, I agree. There are several options for that ranging from full semver awareness and version handling in WordPress, to pushing the version management aspects to developers and external tools entirely, and it should definitely be discussed as part of an initial proposal 👍 |
Leaving here a note with one possible way for updating existing WordPress packages to support ES Modules: |
Nicely aggregated migration guide. Thanks for sharing. ❤️ Sindre
I don't have any precise POC with all WordPress setup to present, but I think adopting ESM and ImportMaps gets us closer to be able to use Open Platform means to solve that problem, see https://github.com/WICG/import-maps#multiple-versions-of-the-same-module. For example, individual plugins could contribute their own import maps, to either use versions provided by WP, or use their own. Also, we (as WordPress instance) could generate/resolve those maps ourselves, given the version ranges from a plugin (link in package.json format), we could compare them to the one we have, and either map it to ours or to theirs. I tried to draft the idea in #35630 (comment) |
There is an active proposal for Enhancing the Scripts API with a loading strategy. There is a comment thread that proposes that |
It appears that import maps are supported in all major browsers now. Even Safari seems to support it in the latest version released 2 days ago: https://caniuse.com/import-maps |
We should add a hook for enqueuing the importmap script in the head before any other scripts. What do you all think about this? Also, I think it would be very nice to be able to specify which tags we want to be rendered on the script tags in the |
@luisherranz opened an issue to track progress on the exploration of the necessary low-level primitives to add native support in WordPress for registering and enqueueing JavaScript modules, including generating an import map: |
It's now implemented in WordPress core and will be shipped with the upcoming 6.5 major release as noted in the Interactivity API merge announcement. Major kudos to everyone involved in making that happen 🎉 |
What problem does this address?
Related WordPress Trac ticket: https://core.trac.wordpress.org/ticket/48654.
JavaScript Modules
Resource: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
JavaScript has had modules for a long time. However, they were implemented via libraries, not built into the language. ES2015 is the first time that JavaScript has built-in modules. Older browsers like Internet Explorer 11 didn't support ES Modules. Earlier this year, WordPress dropped support for IE11, which opened the door to using ES Modules in the Gutenberg plugin and later in WordPress core. The most significant benefit of moving the codebase to modules is the possibility of using native browser techniques for async loading scripts
Import Maps
Resource: https://wicg.github.io/import-maps/
There's native support for import maps in the latest versions of Chrome and Edge browsers, and a shim is available for any other browser that WordPress supports. In practical terms, import maps let use shared libraries by importing dependencies through a predefined list of aliases.
What is your proposed solution?
A long-term goal would be to use JavaScript modules and import maps for all Gutenberg plugin scripts. @youknowriad did some very promising initial explorations:
The text was updated successfully, but these errors were encountered: