To be clear, I am not saying remove jQuery from our stack - this is complicated just as removing jQuery UI is proving to be (T49145). We can continue to support jQuery in our stack but we must make changes now to prepare for a future where we may not want it.
Background
A vanilla MediaWiki instance loads 57.2kb of JavaScript. 42.4kb of this is jQuery to render Special:BlankPage. It seems strange to load jQuery unconditionally and we should be moving towards making jquery a dependency like everything else. It seems the only reason it isn't is because mediawiki.base uses jQuery for 3 lines of code - to make use of $.Deferred, $.ajax and $.Callbacks. It seems that these could be replaced with a lightweight fetch/promise polyfill. One this is done, 'jquery' should be added as an explicit dependency to all those that need it (probably as part of a change to ResourceLoaderFileModule to support backwards compatibility)
More Background
As various teams, myself included, begin using ES5 it's becoming clear that jQuery is not as useful in parts of our stack. In mobile in particular I'm keen for us to defer the loading of jQuery till after firstInteractive to optimise our critical path (see also T127328).
In mobile, there are few of the browser quirks that desktop relies on jQuery to resolve; and native JavaScript methods are more widely supported. Browsers also have regular update paths, meaning browsers are getting more powerful much quicker than desktop. In mobile the use of the jQuery global has been limited to various places by eslint. When we inevitably support ES6, (which will bring Object.assign) our usage will be even more minimal, and soon it will be hard to justify including jQuery. Other large projects with less technical debt and younger than us than us are achieving just this (recently Github: https://twitter.com/mislav/status/1022058279000842240)
As a skin developer, I would also like to build a skin that doesn't depend on jQuery. Freedom to innovate is important and this doesn't seem to be possible in our current ecosystem.
It's clear (at least to me!) that jQuery is not going to be as important in the future. Strategically we should be untangling our mandatory MediaWiki code from jQuery and make this a dependency that RL modules must declare. This is also important for developments in progressive web apps and external mediawiki libraries and services, for which we will want to make use of common MediaWiki libraries for the API without the forced requirement of jQuery). Already we find ourselves building wdio-mediawiki using other libraries (mwbot) instead of code we ship to our production users.
The fallback skin (which has no interactivity) loads 79.4kb of JavaScript (35.8kb is the startup module and 43.9kb jQuery+mediawiki)
At minimum we should provide an abstraction between jQuery and MediaWiki to allow 3rd parties to use MediaWiki without
We should seek alternatives to jQuery in our core code where ES5/6, non-jQuery plugin libraries exist.
Much of our code needs modernising. For instance, we use $.map in various places even though Array.prototype.map is available in all the browsers we support.
jQuery's plugin architecture and use of chaining makes it hard to gauge usage of the library, but actually our reliance on it for our critical code is minimal and it's essential that we
Benefits
- Our libraries become more useful outside the MediaWiki ecosystem e.g. in NPM
- We allow experiences that do not depend on jQuery
- We make our code more malleable and easy to change and react to changes in frontend tech.
Implementation proposal
- Add an interface library that provides library agnostic functionality, where jQuery/browser standards overlap.
This should cover (but not be limited to): adding events, DOMElement selector, Deferred, extend
MobileFrontend already does this to limit and control its jQuery usage:
https://github.com/wikimedia/mediawiki-extensions-MobileFrontend/blob/master/resources/mobile.startup/util.js#L11
Here is a crude limited version of what this will look like:
mw.helpers = { querySelectorAll: function ( selector ) { return $( selector ); }, Deferred: function () { return $.Deferred(); }, extend: function () { return $.extend.apply( $, arguments ); } }
In future as browser support improves it might look like:
mw.helpers = { querySelectorAll: function ( selector ) { return ourCustomMixin( document.querySelectorAll( selector ) ); }, Deferred:function () { return new Promise( .... }, extend: function () { return Object.assign.apply( null, arguments ); } }
- Update existing critical JS to use new helper library or native JavaScript where possible:
- The mediawiki.base module uses $.Deferred() and $.Callbacks
- ext.eventLogging (querySelectorAll, $.append, $.html, $.createElement, $.click, $.hide, $.extend, $.type, $.noop, $.Deferred)
- Replace jquery.accessKeyLabel jquery.client jquery.cookie and jquery.throttledebounce with non-jQuery dependent solutions
- Replace jquery.msg with an on-jQuery dependent solution
- mediawiki.Title ($.extend, $.inArray, $.type, querySelectorAll equivalent)
- mediawiki.Uri should use Array.forEach instead of $.each and mw.helpers.extend rather than $.extend. Remove usage of $.isPlainObject as decided in T192623
- mediawiki.api ($.extend, $.Deferred, $.ajax) including the more complicated mediawiki.api.upload ($.each, $.prop, $.on, $.one, $.css, $.createElement, querySelectorAll, $.addClass, $.remove)
- mediawiki.experiments ($.isEmptyObject)
- mediawiki.jqueryMsg ($.createElement, $.attr, $.map, $.each, $.append, $.text $.click)
- mediawiki.language ($.extend)
- mediawiki.template ($.parseHTML)
- mediawiki.user ($.extend, $.Deferred)
- mediawiki.util ($.noop, querySelectorAll, $.parseHTML, document.ready, $.wrap, $.attr, $.append, $.hasClass, $.removeClass, $.click, $.params)
- mediawiki.viewport (querySelectorAll, width, height)
- mediawiki.page.startup (querySelectorAll)
- Proof of concept
- It should be possible to remove the jQuery dependency from the mediawiki.base module and replace it with another library that implements an equivalent interface e.g. using native JS
- It should be possible for a skin to extend ResourceLoaderStartUpModule with a different startup module package that does not include jQuery.
Measuring success
We can measure success by looking at the amount of bytes the fallback skin ships.