ResourceLoader/Developing with ResourceLoader
This is a quick walkthrough about the key points you need to understand to develop in MediaWiki core or an extension with ResourceLoader.
Contents |
[edit] Registering
- See also Manual:$wgResourceModules
In order for all the ResourceLoader features to function properly it is required to register modules under a symbolic name. We do that by adding the module definition to $wgResourceModules
(if you're writing an extension), or to add the array to ./resources/Resources.php (if you're working in core).
$wgResourceModules['ext.myExtension'] = array( // JavaScript and CSS styles. To combine multiple files, just list them as an array. 'scripts' => array( 'js/ext.myExtension.core.js', 'js/ext.myExtension.foobar.js' ), 'styles' => 'css/ext.myExtension.css', // When your module is loaded, these messages will be available through mw.msg() 'messages' => array( 'myextension-hello-world', 'myextension-goodbye-world' ), // If your scripts need code from other modules, list their identifiers as dependencies // and ResourceLoader will make sure they're loaded before you. // You don't need to manually list 'mediawiki' or 'jquery', which are always loaded. 'dependencies' => array( 'jquery.ui.datepicker' ), // You need to declare the server base path to the files mentioned // in the 'scripts' and 'styles' references above. // __DIR__ is a server path reference to where the file that contains this array is at. // so, if this code is in /var/www/extensions/MyExtension.php // then the js references above would be to // /var/www/extensions/js/ext.myExtension.core.js and // /var/www/extensions/js/ext.myExtension.foobar.js 'localBasePath' => __DIR__, // ... and the base from the browser as well. For extensions this is made easy, // you can use the 'remoteExtPath' property to declare it relative to where the wiki // has $wgExtensionAssetsPath configured: 'remoteExtPath' => 'MyExtension' );
Notice that we only list the dependency we directly need. Other modules such as jquery and jquery.ui will be automatically loaded to support jquery.ui.datepicker. Also notice that because (in this example) ext.myExtension is a module provided by and related to an extension, the module name begins with "ext.
".
Tip: Pass single resources as a string. Pass multiple resources as an array of strings.
[edit] CSS parsing
[edit] Annotations
[edit] @embed
- See also ResourceLoader/Features#Embedding
The @embed
annotation triggers the Data URI embedding feature of ResourceLoader. Small images (up to 24kb per image) referenced with this annotation will be automatically embedded by ResourceLoader.
.mw-foo-bar { /* @embed */ background: url(images/icon-foo-bar.png) 0 50% no-repeat; padding: 4px 0 3px 40px; }
[edit] @noflip
- See also ResourceLoader/Features#Flipping
To disable the flipping functionality for one CSS declaration or on an entire ruleset, use the @noflip
annotation:
For example:
/* @noflip */ .mw-portlet-item { line-height: 1.25em; float: left; } /* This one flips! */ .mw-portlet-item { margin-left: 0.75em; /* ... except this one: */ /* @noflip */ margin-top: 0.5em; font-size: 0.75em; white-space: nowrap; }
[edit] Module structure
- Work in progress
- Top module
Two modules for most extensions. A module to be loaded from the <head>
containing styles and/or scripts that need to run as soon as possible. This module should generally be a small as possible and be used to:
- Style output by PHP
- Insert or modify elements that change the location of anything above the "the fold" (such as inserting a banner that pushes the page down, or a sidebar widgets that pushes another sidebar portlet down, or a content action tab that causes a change in the position of other tabs)
- Bottom module (default)
- Insert or modify elements that are not visible right away (e.g. binding autocompletion to a form element or inserting a small absolutely positioned widget)
- Anything else
[edit] Debugging
[edit] Toggle debug mode
ResourceLoader supports complex client-side web applications in production and development environments. As these different environments have different needs, ResourceLoader offers two distinct modes: production mode and development mode (also known as "debug") mode.
Development mode is designed to make development as easy as possible, prioritizing the ease of identifying and resolving problems in the software over performance. Production mode makes the opposite prioritization, emphasizing performance over ease of development.
It is important to test your code in both development and production modes. In day-to-day development, most developers will find it beneficial to use development mode most of the time, only validating their code's functionality in production mode before committing changes.
Learn more about debug mode at ResourceLoader/Features#Debug mode.
[edit] Server-side errors appear in comments
ResourceLoader reports PHP exceptions in JavaScript comments. This can mean that if there's a failure in PHP, unrelated JavaScript code reports an error, because the functionality it depends has been replaced by a comment containing the error. You should view the source of loaded modules when debugging an error to see if it includes PHP errors.
[edit] Breaking cache
When making frequent changes to code and checking them in a browser, the caching mechanisms designed to improve the performance of web-browsing can quickly become inconvenient. When developing on a system which is not making use of a reverse proxy such as Squid or Varnish, you only need to force your browser to bypass its cache while refreshing. This can be achieved by pressing CTRL+F5 in Internet Explorer, or holding the shift key while clicking the browser's refresh button in most other browsers.
If you are developing behind a reverse proxy, you can either change the values of $wgResourceLoaderMaxage or use ?debug=true to bypass cache since bug 25573 is fixed.
[edit] Good examples
The following is a list of MediaWiki extensions which take advantage of ResourceLoader functionality and are known to be a good example of ResourceLoader integration.
[edit] Loading module
[edit] Server-side
While building the page, if you need a module to be loaded, you need tell the OutputPage object to add one or more modules to the page by calling the addModules
methods and passing one or more (variadic) arguments (such as "mediawiki.foo
", "jquery.bar
" or "ext.myExtension.quux
")
$wgOut->addModules( 'example.fooBar' );
OutputPage::addModules
adds the given the module name(s) to the load queue of the page. The client side loader will request all of the components for this module (scripts, styles, messages, dependencies, etc.) and load them correctly. If your module contains a stylesheet that styles elements that are outputted by PHP as well as styles and/or scripts that insert content dynamically, then you should split the module into two separate modules. One for styling/enhancing the output, and one for dynamic stuff. The former module should have the "position" property set to "top" (in the module definition in $wgResourceModules
), so that ResourceLoader will load it before parsing the rest of the HTML, preventing a FOUC (Flash of unstyled content). The other module doesn't need a "position" property and will simply be loaded asynchronously by the client, not blocking further parsing of the page.
Both should be added to the load queue in the same way, without repeating or forcing any particular loading style. This information is already in the module registry and the client will handle it accordingly. Usage of OutputPage::addModuleStyles
should be avoided, unless you need the CSS to be loaded when JavaScript is disabled.
$outputPage->addModules( 'ext.myExtension.foo', 'ext.myExtension.bar' );
[edit] Client-side (dynamically)
If you only need a module in a certain scenario of the user interface, you could instead create a small init module (that is loaded server side), and from there use JavaScript to kick-off the load of the rest of the module when necessary. Use the mw.loader
object for this. To access functions and variables defined in the module's scripts from the callback scope — they need to be made public, i.e. declared in the window
object.
Module body:
window.foo = function () { alert('bar!'); };
Usage:
mw.loader.using( 'example.fooBar', function () { /* This callback is invoked as soon as the example.fooBar is ready */ foo(); } );
Tip: If you just want to load the module, and don't need the callback, you can use mw.loader.load( 'example.fooBar' );
instead.
[edit] See also
$wgResourceModules
: Array of all extra defined modules that can later be loaded during the output$wgExtensionAssetsPath
: Path to extension assets - must be set for extensions to work in debug mode.$wgResourceLoaderMaxage
:$wgResourceLoaderInlinePrivateModules
:$wgResourceLoaderDebug
:$wgResourceLoaderUseESI
:- ResourceLoader (especially Developing with ResourceLoader to learn how to use it in MediaWiki core or an Extension)
- Manual:Hooks/ResourceLoaderRegisterModules
ResourceLoader | |
---|---|
Documentation | Features · Vocabulary · Migration guide (users) · Migration guide (developers) · Developing with ResourceLoader · Default modules |
Project information | Status updates · Version 1 Design Specification (tasks) · Version 2 Design Specification (tasks / tests) · Requirements |
Other | JavaScript Deprecations |