There are currently no guidelines on MediaWiki.org around how to load JavaScript (specifically Codex) on the page.
Context
The Codex library is currently fragmented into codex-search (JS+CSS limited component version), codex-search-styles (CSS only limited), codex-styles (CSS only), @wikimedia/codex (JS + CSS). The design systems team is adding code split as a deliverable by the end of 2023. This will allow extensions/skins/gadgets to load Codex on a per component basis.
The following components are now loading Codex (or a subset of Codex) on page load on the article namespace:
- QuickSurveys
- FlaggedRevisions
- MobileFrontend
- Minerva
- Vector
Questions to answer
- Should we continue to load Codex and Vue.js on demand (e.g. when something is clicked) or would it make sense to load the entire library by default (e.g. as we do with jQuery)
- If we are loading the code on demand, what constitutes an acceptable user interaction to load (e.g. clicking a button, scrolling page, waiting for a certain time to pass)
- When adding Codex to the page, is is preferable to load the entire library or a subset of the library? What does this mean for cache fragmentation (T350514#9321359)
- What does an optimized user experience look like?
- At what point should we re-evaluate our strategy for loading Codex? (See T350514#9323124)
History
Codex is gaining adoption throughout MediaWiki. While predominately it is loaded on special pages, a limited CSS only version is currently being loaded on articles for anonymous users inside Vector and Minerva.
WVUI (the precursor to Codex) was used to build out the Vector 2022 search experience in February 2021. The WVUI library was loaded when the user focused or typed into the search box. For server side rendering, the web team maintained a CSS-only version of WVUI which had to be kept in sync with the WVUI library.
Around the same time QuickSurveys which loads survey's in articles was revised to use the WVUI library.
In July 2021, the RelatedArticles codebase was rewritten in WVUI (T286835). Merging the patch was blocked due to a concern that RelatedArticles loaded the entirety of WVUI when the user scrolled to the bottom of the page. At the time (see discussion) we debated whether a user scroll met the criteria of a "user interaction" (see discussion below).
In July 2022, WVUI became Codex. QuickSurveys and Vector 2022 were migrated to the new library.
In March 2022 the RelatedArticles patch was revised to use Codex instead of WVUI.
Since June 2023, FlaggedRevisions now uses Codex and loads the entire library styles on article namespace. Due to a lack of guidelines, this resulted in loading 2 versions of the Codex library on article pages (T350514).
In November 2023 the RelatedArticles patch was revised to use Codex Codex CSS components.
Discussion
I had a useful conversation with Timo today [2020-03-27] but would love to see this documented.
[11:28 AM] @Krinkle wrote:
Jdlrobson: It's generally either addModules on page load, or mw.loader.using() to lazy load upon user interaction. There's no other strategies afaik needed currently.
these two are documeted at https://www.mediawiki.org/wiki/ResourceLoader/Developing_with_ResourceLoader#Loading_modules
How to detect MobileFrontend is imho out of scope for RL. That's part of core conventions for ExtensionRegistry (in PHP) and based on what promises/public stable interfaces MF wants to expose/maintain client-side.
Any kind of conditional is fine imho.
[11:33 AM] @Krinkle wrote:
Right, that's less about how to implement a certain direction, but what direction to pick overall.
When something is needed on page load, it's addModules(). There's only one way to do that really, and that's documented. To lazy-load, you use mw.loader.using().
I don't see how it ties in with desktop/mobile target stuff. Why are we looking to change how existing code is loaded as part of that?
I agree we can use better docs around that, and will try to find it and/or write it. In general, later is better. And loading Vue.js on page load is imho a no-go no matter what.
requestIdleCallback was a one-off experiment for Popups and shuld be removed, not recommended for any circumstance.
(There's a task about that)
[11:55 AM] @Krinkle wrote:
Yeah, I'll see what I can do, but ultimately it comes down to budgetting and performance review per-feature. I don't think we can describe clearly when it is fine to load on the page and when it isn't. In general, nothing should load on the page, unless it has to do something on that page and that it has to do it immediately when the page loads. That in itself is generally already something that should be avoided as it will not arrive for all users (on slow devices they can comfortably read a paragraph and move on to another page before it arrives), so whenever possible first try pure CSS-only etc. But if it has to be client-side, and has to be on page load, and doesn't result in bad UX, then loading the minimum needed on the pages right away is probably fine, but that still depends on where we are in our budget and how computationally expensive the code is, and how large it is.
(Feel free to ignore this, I'm just drafting out loud)
Things that fall under this is: "init" code that adds event handlers to the Edit button click event, to the Search input focus event. Code that makes the reserved space for sortable icons in tables filled in with an actual button and makes those buttons work. (Slight contradiction, why is it okay to not lazy-load jquery.tablesorter? This is a trade-off in size of the code, overhead of having yet another init module, and latency when the user clicks it given there is nothing to hide that latency behind, it is just sorting. Whereas search and edit do more things behind the scenes that already involve the network, so loading the code async is fine there. This is why the "init" code of VE isn't just a click handler, it also includes very minimal CSS to make that click result in the page looking like it is "loading".
also, I have a proposal to kill all "init" modules in favour of a standard attribute that will do this for you. E.g. you have an attribute in the HTML, when that is clicked, it will add a class that allows your CSS to make it look like however it should look when it is loading, and then when the code arrives it will call a certain method. The logic for this would be generic and only written once. See more at https://phabricator.wikimedia.org/T183720
This would mean we can remove almost all JS from page load, except for stuff that does non-user facing things (like navtiming metrics), and init code for which the loading state is very complex (more than a simple class name switch can do).
also, this will encourage use of "delegate event handlers". right now, a very common pattern (albeit a bad one) is to load JS that waits for dom-ready and then selects all of something and calls on(). This is problematic for many reasons. 1) It's poor UX because it means that when the JS has arrived, and most of the page is there and painted, the JS just sits there idle and the user can't click stuff. It waits for the whole page to be there below the fold, until 2) it selects all the relevent elements which is expensive and slow, and 3) allocates many event handlers, again expensive. Whereas if we use a delegate handler like $(document.body).on('click', '.my-stuff') you can run that right away without waiting for dom-ready, and allocates only 1 event handler, and any elements that exist now or in the future that match will just work, because we check it at click-time, instead of at select-time.
If I recall correctly, we adopted this in a few places last year around the Cite instrumentation that reading was involved with.