Page MenuHomePhabricator

[MEX] M5 - Spike - Reduce load for low data users
Open, Needs TriagePublic

Description

Target user for MEX is for wikidata editors who would like to edit on their mobile devices. Many mobile devices do not have a strong/reliable internet connection or large data packages.

Our current implementation involves loading templates twice and its unclear what we're asking devices to load at what point

Goal of this spike is to review how we're doing things and if we can improve the situation for low data users

  • If there are quick wins then they could be fixed as part of the spike
  • If there is a larger change needed, then this should be pulled into a new ticket

Risk: Premature optimization. Lets focus on keeping prototyping fast.
Are there two convos here? reduce load and double loading of templates. Maybe reduce load is better suited for M3- Editing Functionality

Acceptance Criteria

  • Spike results are reported in this ticket
  • The double load situation is confirmed as necessary or unnecessary

Timebox: 16 hours

Event Timeline

Not sure exactly how relevant this is for the ticket, but something I occasionally come across is discussions of the Islands Architecture - https://jasonformat.com/islands-architecture/ . It seems to speak to some of what our concerns here are, and there are some more recent resources for it: https://is-land.11ty.dev/ , including demos using Vue: https://is-land.11ty.dev/demo-vue .

Our current implementation involves loading templates twice

I’m not sure what this is referring to…

Arian_Bozorg subscribed.

There is double work happening:

  • the server renders the html templates
  • they are rerendered again with the front end code

Story writing notes:
This may be best to look at once we start working on once we begin on the editing functionality

This needs some more story writing

Arian_Bozorg renamed this task from [MEX] Spike - Reduce load for low data users to [MEX] M5 - Spike - Reduce load for low data users.Jan 23 2026, 1:16 AM

I instrumented the sortDependencies function in mediawiki.loader.js to try and track down where our oojs dependencies are coming from, and it looks like removing that won't be all that simple:

  • mobile.startup
    • mediawiki.page.watch.ajax
    • mediawiki.router
      • oojs
    • mediawiki.storage
    • mediawiki.template.mustache
    • mobile.codex.styles
    • mobile.init.styles
  • wikibase.wbui2025.lib
    • mediawiki.ForeignApi
      • mediawiki.ForeignApi.core
        • mediawiki.api
        • oojs
    • pinia
    • wikibase
  • wikibase.client.data-bridge.init
    • mw.config.values.wbDataBridgeConfig
    • oojs-ui-windows
      • oojs-ui-core
        • mediawiki.page.ready
        • oojs
        • oojs-ui-core.icons
        • oojs-ui-core.styles
        • oojs-ui.styles.indicators
      • oojs-ui-windows.icons
  • wikibase.quality.constraints.gadget
    • oojs-ui.styles.icons-alerts
    • wikibase.quality.constraints.icon
    • wikibase.quality.constraints.ui
      • jquery.makeCollapsible
      • oojs-ui-widgets
        • oojs-ui-core
          • mediawiki.page.ready
          • oojs
          • oojs-ui-core.icons
          • oojs-ui-core.styles
          • oojs-ui.styles.indicators
        • oojs-ui-widgets.icons
        • wikibase

So even if we cleaned up our own direct usage of ooui (from Data Bridge and Quality Constraints), we would still be pulling in oojs because mobile.startup and mediawiki.ForeignApi use it.

OOJS is much smaller than OOUI though.

So I think eliminating OOUI dependencies would still be quite valuable even if OOJS remains.

Fair. If I disable quality contraints and data bridge, oojs-ui-* does indeed not get loaded. I'll take a look at whether it's possible to make those loads conditional on (the absence of) MEX.

Change #1268552 had a related patch set uploaded (by Arthur taylor; author: Arthur taylor):

[mediawiki/extensions/WikibaseQualityConstraints@master] Do not load oojs-ui for the new mobile (MEX) interface

https://gerrit.wikimedia.org/r/1268552

That said, on my wiki (and also on Wikidata), oojs-ui-widgets is also pulled in by ext.echo.ui, so we won’t get rid of it via WBQC alone. (@ArthurTaylor I’m guessing you don’t have Echo installed?)

for ( const [ name, module ] of Object.entries( mw.loader.moduleRegistry ) ) { if ( module.state !== 'ready' ) continue; if ( module.dependencies.includes( 'oojs-ui-widgets' ) ) console.log( name ); }

Weird. I do have echo installed, and don't see oojs-ui-widgets load if I disable Kartographer / Data Bridge and apply my patch.

I generated Lighthouse reports for MEX and Desktop against prod for a simple item (https://www.wikidata.org/wiki/Q37315170), and it looks like we score okay. MEX has a couple more accessibility issues than the desktop interface, but those shouldn't be too hard to fix (and are not in the scope of this ticket)

2026-04-08-085655_372x141_scrot.png (141×372 px, 10 KB)

2026-04-08-085705_368x137_scrot.png (137×368 px, 11 KB)

For the Sandbox Item the results look much worse, and worse for MEX than for Desktop, so it seems like something is systematically worse for MEX. Will try and dig into that.

2026-04-08-090612_382x137_scrot.png (137×382 px, 13 KB)

2026-04-08-090619_389x133_scrot.png (133×389 px, 12 KB)

Change #1268913 had a related patch set uploaded (by Arthur taylor; author: Arthur taylor):

[mediawiki/extensions/Wikibase@master] [WIP] Load MEX edit components asynchronously

https://gerrit.wikimedia.org/r/1268913

Added a patch with a proof of concept for lazy-loading the interactive Vue elements - should speed up the first load and read-only experience, with only a slight delay for users that then click on edit or add statement.

In fact, we could delay the code in entityViewInit.js / wikibase.entityPage.entityLoaded until someone clicks edit or add, and if we render the 'add statement' button on the server-side, we wouldn't actually need to load vue / pinia at all until someone clicks either add or edit (at the expense of making those actions slightly heavier).

Re. double load. It is certainly the case that in the current implementation, we send the layout for the MEX sections as rendered by php-vuejs-templating in the initial HTML:

http://wikidatawikidev.mediawiki.local.wmftest.net:8080/w/index.php?title=Q15&debug=2

<div class="wikibase-wbui2025-statement-section" data-section-key="statements" data-props="P70">
  <div class="wikibase-wbui2025-statement-section-heading"><h2 class="wb-section-heading section-heading wikibase-statements" dir="auto" id="claims">Statements</h2></div>
  <div class="wikibase-wbui2025-statement-section-content">
    <div id="wikibase-wbui2025-statementwrapper-P70">
      <div class="wikibase-wbui2025-statement-group">		
        <div class="wikibase-wbui2025-statement-heading">
          <div class="wikibase-wbui2025-statement-heading-row">
            <p>
              <span class="wikibase-wbui2025-property-name">
                <span class="wikibase-wbui2025-property-name-link" data-property-id="P70"><a title="Property:P70 (page does not exist)" href="/w/index.php?title=Property:P70" class="new">P70</a> <span class="wb-entity-undefinedinfo">(Deleted Property)</span></span>
	      </span>
            </p>
            <div class="wikibase-wbui2025-link wikibase-wbui2025-edit-link">
              <span class="wikibase-wbui2025-icon-edit-small"></span>
              <span class="wikibase-wbui2025-link-heavy">
                edit
              </span>
            </div>
          </div>
        </div>
        <div id="Q15$e2074934-4032-ff71-85ef-adf2c64f415d" class="wikibase-wbui2025-statement-view">
          <div class="wikibase-wbui2025-main-snak">
            <div class="wikibase-wbui2025-rankselector">
              <span class="wikibase-rankselector-normal" title="Normal rank"></span>
              </div>
              <div class="wikibase-wbui2025-snak-value" data-snak-hash="0077265e471ec798f2e671de3b5d7c1324c4c819">
                <span class="snakValue">test string</span>
              </div>
            </div>
            <div v-if="hasQualifiers" class="wikibase-wbui2025-qualifiers">
            </div>
            <div class="wikibase-wbui2025-references">
              <p class="">
                <span>0 references</span>
              </p>
            </div>
          </div>
        </div>
      </div>

and then send the templates for the same layout elements when Vue loads:

http://wikidatawikidev.mediawiki.local.wmftest.net:8080/w/load.php?debug=2&lang=en&modules=wikibase.wbui2025.entityViewInit&skin=minerva&version=ztntf

...
module.exports.template = "<div class=\"wikibase-wbui2025-statement-section\" :data-section-key=\"sectionKey\" :data-props=\"implode( ',', propertyList )\"> \n\
		<div class=\"wikibase-wbui2025-statement-section-heading\" v-html=\"sectionHeadingHtml\"><\/div> \n\
		<div class=\"wikibase-wbui2025-statement-section-content\"> \n\
			<div v-for=\"propertyId in propertyIds\" :id=\"concat( 'wikibase-wbui2025-statementwrapper-', propertyId )\" :key=\"propertyId\"> \n\
				<wbui2025-statement-group-view :property-id=\"propertyId\" :entity-id=\"entityId\"><\/wbui2025-statement-group-view> \n\
			<\/div> \n\
			<wbui2025-add-statement-button v-if=\"javaScriptLoaded\" :entity-id=\"entityId\" :section-key=\"sectionKey\"><\/wbui2025-add-statement-button> \n\
		<\/div> \n\
	<\/div>";
...
module.exports.template = "<div :id=\"statementId\" :class=\"activeClasses\"> \n\
		<wbui2025-main-snak :main-snak=\"statement.mainsnak\" :rank=\"statement.rank\" :statement-id=\"statementId\"><\/wbui2025-main-snak> \n\
		<wbui2025-qualifiers :qualifiers=\"qualifiers\" :qualifiers-order=\"qualifiersOrder\" :statement-id=\"statementId\"><\/wbui2025-qualifiers> \n\
		<wbui2025-references :references=\"references\" :statement-id=\"statementId\"><\/wbui2025-references> \n\
	<\/div>";
...
module.exports.template = "<div class=\"wikibase-wbui2025-references\"> \n\
		<p :class=\"{ 'wikibase-wbui2025-clickable': hasReferences }\" @click=\"showReferences = !showReferences\"> \n\
			<span v-if=\"hasReferences\" :class=\"{ 'wikibase-wbui2025-icon-expand-x-small': !showReferences, 'wikibase-wbui2025-icon-collapse-x-small': showReferences }\"><\/span> \n\
			<a v-if=\"hasReferences\" href=\"javascript: void(0)\" class=\"wikibase-wbui2025-link\">{{ referencesMessage }}<\/a> \n\
			<span v-else=\"\">{{ referencesMessage }}<\/span> \n\
		<\/p> \n\
		<div v-if=\"hasReferences\" class=\"wikibase-wbui2025-reference-list\" :class=\"{ 'wikibase-wbui2025-references-visible': showReferences }\"> \n\
			<template v-for=\"reference in references\" :key=\"reference\"> \n\
				<div class=\"wikibase-wbui2025-reference\"> \n\
					<template v-for=\"propertyId in reference['snaks-order']\" :key=\"propertyId\"> \n\
						<div v-for=\"snak in reference.snaks[propertyId]\" :key=\"snak\" class=\"wikibase-wbui2025-reference-snak\"> \n\
							<wbui2025-property-name :property-id=\"propertyId\"><\/wbui2025-property-name> \n\
							<wbui2025-snak-value :snak=\"snak\" :statement-id=\"statementId\" :reference-hash=\"reference.hash\"><\/wbui2025-snak-value> \n\
						<\/div> \n\
					<\/template> \n\
				<\/div> \n\
			<\/template> \n\
		<\/div> \n\
	<\/div>";

For items with only one or a few statements, the markup we send for the templates is even larger than the static-rendered HTML. As the number of statements on the item increases, the static markup dominates the content and the templates are only a fraction of the data sent to the client. But per T400325#11798263, I think it should be possible to avoid sending the Vue and templates at all until someone clicks edit / add, and it's certainly possible to avoid sending all templates.

What is certainly the case is that in entityViewInit.js, we mount a hydrated Vue app over each .wikibase-wbui2025-statement-section, only to put the same content in it that already exists on the static page. So if I interpret "double-load" as sending the static content, sending the template, and then hydrating the template (replacing the existing DOM elements with identical Vue-rendered elements), I would say that this is for sure happening, and probably unnecessary if we are happy with an on-demand hydration.

Spike findings

MEX has some room for easy optimisations that would improve the experience for low-data users / users with less powerful devices. The page load, especially for items with many statements, is not optimal, but it is important to focus on aspects that are within the control of Wikibase / MEX to avoid spending a lot of time optimising Wikibase / Desktop or Mediawiki in general (both of which would be out of scope for this ticket).

Low-hanging fruit

  • Avoid loading oojs-ui-* libraries. Per T400325#11793469, we can save some data by avoiding loading especially oojs-ui-widgets in WikibaseQualityContraints for the MEX interface. This patch demonstrates what that approach might look like, and there are already review comments on the patch highlighting the issues. As long as we have the data bridge enabled (which is the case on Wikidata in production), we will continue to have a dependency on oojs-ui-windows (and by transitivity on oojs-ui-core) - it might be worth separately investigating what the effort required would be to remove the data bridge oojs-ui-* dependency in the MEX case.
  • Avoid loading Vue partially or entirely until a user clicks on an "edit" link or on the "add statement" button. Per T400325#11798263, we do not need to load the whole MEX Vue UI on page-load, and might be able to avoid loading Vue entirely until a user triggers interactive features. This would speed up the read-only experience, but might slow down the interactive experience (since we are not loading all assets at once) - we need to decide who our target is for this optimisation and what our preferred experience is.

"Double Load"

Per T400325#11803012, we are to some extent duplicating work (bytes, CPU usage) when we send static content and repaint Javascript-rendered versions of the same content. Depending on the experience we are trying to optimise for, we could either avoid sending the static content (making MEX Javascript-only, which might be a safe assumption for the mobile devices we are targeting, and is anyway a requirement for users to be able to edit, which is the goal), or avoid loading Vue until the user has clicked on an interactive element (though again, if we are trying to optimise the experience for *editors* on mobile, there is a question whether lazy-loading helps or makes things worse). A question we need to answer is how often editors browse through item pages without editing, and whether speeding up this experience is worth the additional cost of the lazy-loading for the editing flow.

Other optimisations

Per T400325#11797725 and T400325#11797733, page performance reports indicate that there is room for optimisation, especially for Items with a lot of statements. Unfortunately, trying to work out which of these optimisations are within our scope (Wikibase / MEX) and which would require changes to Mediawiki is difficult. The optimisation is further complicated by the fact that we deploy alongside other extensions and features (data bridge, Kartographer, WikibaseQualityConstraints) that have their own contributions to page load performance. My recommendation would be that we focus on aspects that are clearly directly connected to the MEX implementation to ensure that our reimplementation of the mobile experience has at least not made things worse, and accept that we won't be able to address all underlying performance concerns.

As long as we have the data bridge enabled (which is the case on Wikidata in production)

Are you sure? I don’t see any bridge-related ResourceLoader modules on Wikidata. (In the production config, wmgWikibaseRepoDataBridgeEnabled is true on Wikidata, but wmgWikibaseClientDataBridgeEnabled is false.)

  • Avoid loading Vue partially or entirely until a user clicks on an "edit" link or on the "add statement" button. Per T400325#11798263, we do not need to load the whole MEX Vue UI on page-load, and might be able to avoid loading Vue entirely until a user triggers interactive features. This would speed up the read-only experience, but might slow down the interactive experience (since we are not loading all assets at once) - we need to decide who our target is for this optimisation and what our preferred experience is.

+1, this was what I had in mind (but deciding who our targets are is a fair point).

As long as we have the data bridge enabled (which is the case on Wikidata in production)

Are you sure? I don’t see any bridge-related ResourceLoader modules on Wikidata. (In the production config, wmgWikibaseRepoDataBridgeEnabled is true on Wikidata, but wmgWikibaseClientDataBridgeEnabled is false.)

Ah - no, I was just assuming. If it's not there at all then that makes the advantage in fixing QualityConstraints much clearer