Jump to content

Vue.js

From mediawiki.org
(Redirected from WVUI)
MediaWiki version:
1.35

Vue.js is the Wikimedia Foundation's official choice for adoption as the JavaScript framework for use within MediaWiki. This page documents guidelines, best practices, and resources for using Vue within MediaWiki. This is not a general guide for using or developing with Vue.js; for that, see the general Vue.js documentation.

Background

[edit]

Vue.js is a modern JavaScript framework used to build interactive user interfaces with ease and consistency. Its component-based approach, performance, approachable paradigms and documentation, and ecosystem of associated libraries make it a powerful tool for building simple or complex UIs. Since Vue is widely used, using it in MediaWiki means many more developers will be able to contribute. Vue is an open-source project supported by a core team and a community of volunteer contributors.

Milestones

[edit]
  • Early 2019 - Discussions about adopting a modern JavaScript framework into MediaWiki began.
  • December 2019 - An RFC on the subject was started.
  • February 2020 - Vue.js was added to core MediaWiki. The NearbyPages extension, the first MediaWiki extension to use Vue.js, was released soon afterwards.
  • March 2020 - The RFC ended, with the official decision to use Vue.js (though this decision was not clearly announced until August 2021).
  • May 2020 - WVUI (Wikimedia Vue User Interface), the first attempt at a shared library for Vue.js-based components for use within Wikimedia projects, was created.
  • September 2020 - MediaWiki 1.35 was released; it was the first version to include Vue.js, as well as the related Vuex library.
  • September 2021 - A replacement library for Vue.js within Wikimedia, Codex, was started following feedback from WMDE.
  • December 2021 - Vue.js within MediaWiki was upgraded from version 2 to 3.
  • February 2022 - Codex was added to MediaWiki 1.38.
  • April 2023 - WVUI was removed in MediaWiki 1.40.
  • April 2023 - Pinia, the successor library to Vuex, was added to MediaWiki core.
  • October 2023 - Codex 1.0, considered the first fully usable version of Codex, was released.
  • November 2023 - Vue.js within MediaWiki was switched from "compatibility mode" to proper Vue 3.

Guidelines

[edit]
The following guidelines apply to usage of Vue.js inside of MediaWiki

Get Vue.js from ResourceLoader

[edit]

Developers should rely on the version of Vue that is already provided via ResourceLoader rather than bundling or vendoring their own copy of the library. MediaWiki ships with Vue 3.

Developers of skins or extensions which list "vue" as a dependency in extension.json or skin.json can require the library as they would any other RL module.

const Vue = require( 'vue' );

Developers of gadgets, or of any feature where Vue is to be loaded conditionally, can load the Vue module on the client-side:

mw.loader.using( [ 'vue' ] ).then( function ( require ) {
    const Vue = require( 'vue' );
} );

Use single-file components

[edit]

MediaWiki supports Vue single-file components (aka .vue files) within any ResourceLoader module that is using the packageFiles feature. However, MediaWiki's support for single-file components (SFCs) is incomplete, and has the following limitations:

Files/tags:

  • .vue files are only supported in package modules (modules that use packageFiles)
  • Self-closing tags for components (e.g. <my-component />) are not supported. Instead, you have to use open and close tags (e.g. <my-component></my-component>).
  • Component tags using PascalCase (e.g. <MyComponent>) are not supported. Instead, you have to use kebab-case for component names (e.g. <my-component>).

Scripts:

  • <script setup>, syntax used for the Composition API, is not supported. (However, using the Composition API without <script setup> is supported.)
  • Pre-processors, such as <template lang="pug"> or <script lang="ts"> (TypeScript) are not supported.
  • Src Imports are not supported (but support for this feature could potentially be added, if developers find it useful).
  • ES6 import and export are not supported. Instead of import, use require(); instead of export default { ... }; use module.exports = { ... };. Other ES6 syntax is supported.
  • ES2016 and newer syntax is not supported. Notable examples of unsupported syntax include the spread operator in object literals ({ foo, ...bar }), async/await, and Object.values()/Object.entries().

Stylesheets/styles:

  • Advanced style features, such as <style scoped>, <style module>, and v-bind in styles are not supported.
  • Unlike TypeScript, Less preprocessor via <style lang="less"> is supported.
  • Less styles are processed using MediaWiki's Less processor, which uses an old version of Less! This means, though, that all the features supported in other Less files in MediaWiki are supported here too.
  • To access Codex design tokens and mixins inside Vue files, import MediaWiki skin variables @import 'mediawiki.skin.variables.less'; at the beginning of your style section.

If you try to use an unsupported SFC feature, ResourceLoader will throw an error at runtime. Unsupported JS or template syntax results in a lint error, so make sure you set up linting for your code to catch these, as well as other pitfalls and style guideline violations.

Initialise with createMwApp()

[edit]

To mount your component to the DOM, use Vue.createMwApp(). This function works the same as Vue.createApp(), but adds shared MediaWiki-specific plugins for internationalisation and error logging. The code mounting your component should be in a simple init file that requires the top-level component (plus any stores and plugins) and mounts it to a placeholder div. Other logic and UI code should live in other files (for example, the top-level component) as much as possible. Conventionally, the top-level component file is called App.vue and the init file is called init.js, unless there are multiple entry points that each have their own top-level component.

A simple example of an init file would look like this:

var Vue = require( 'vue' ),
    App = require( './App.vue' );

// Assuming there's a <div id="my-component-placeholder"> on the page
Vue.createMwApp( App ).mount( '#my-component-placeholder' );

A more complex init file that passes props to the root component, uses a custom plugin and a Pinia store would look like this:

var Vue = require( 'vue' ),
    App = require( './App.vue' ),
    rootProps = { foo: 1, bar: 2 },
    fooPlugin = require( './plugins/foo.js' ),
    Pinia = require( 'pinia' ),
    pinia = Pinia.createPinia();

Vue.createMwApp( App, rootProps )
    .use( fooPlugin )
    .use( pinia )
    // Assuming there's a <div id="my-component-placeholder"> on the page
    .mount( '#my-component-placeholder' );

Don't rely on build tools or server-side rendering

[edit]

In the wider Vue.js community, developers make frequent use of Node.js build tools like Vite, Rollup, Nuxt.js, etc. MediaWiki is a PHP application, and currently no Node.js service is available for use in production in WMF projects. For the time being, developers working with Vue in MediaWiki must limit themselves to client-side functionality only. The Design Systems Team is currently exploring ways to support a front-end build step or server-side component rendering, but this remains experimental.

Use Pinia for state management

[edit]
MediaWiki version:
1.41

Pinia will be made available in MediaWiki in v1.41. Pinia is the new official state management library for use with Vue 3 and successor to Vuex 4. Pinia is recommended for new projects. For existing Vuex-based projects, MediaWiki also makes Vuex 4 available. See also the guide for Vuex to Pinia migrations.

Follow MediaWiki coding conventions

[edit]

See MediaWiki's Vue coding conventions, which are largely based on the official Vue Style Guide. We use ESLint to enforce these conventions; see here for how to set up ESLint in your repository.

Internationalization

[edit]

The i18n plugin

[edit]

The vue module comes with a small i18n plugin that wraps MediaWiki's i18n system (mw.message), so you can use MediaWiki i18n messages in your templates. This plugin creates an $i18n() function that you can use inside templates, which is an alias for mw.message(). For plain text messages (most cases), you can simply use:

<p>{{ $i18n( 'message-key' ).text() }}</p>

You can pass parameters either variadically, or as an array:

<p>{{ $i18n( 'message-key', param1, param2 ).text() }}</p>
<!-- or: -->
<p>{{ $i18n( 'message-key' ).params( [ param1, param2 ] ).text() }}</p>

The $i18n() function returns a Message object, so you can use it in all the same ways that you can use mw.message() in "normal" JS code. Remember that all message keys you use have to be added to the "messages" array in the ResourceLoader module definition.

Parsed messages

[edit]

Because parsed messages return HTML rather than text, they have to be invoked differently:

<!-- Parsed message without parameters: -->
<p v-i18n-html:category-empty></p>

<!-- Parsed message with array of parameters: -->
<p v-i18n-html:namespace-protected="[ namespaceName ]"></p>

<!-- Parsed message with dynamic message name: -->
<p v-i18n-html="msgNameVariable"></p>

Note that <p>{{ $i18n( 'category-empty' ).parse() }}</p> doesn't work, because the {{ variable }} syntax only works for text, not HTML. If you try to use a parsed message this way, the user will see the resulting HTML as plain text.

See the documentation comments in resources/src/vue/i18n.js for a more detailed explanation of v-i18n-html.

Complex use cases

[edit]

If the parameters to a message are complex, or if both the message name and its parameters are dynamic, it may be better to create a computed property for the message, as follows:

<template>
    <p v-i18n-html="fooOrBarMsg"></p>
</template>
<script>
const { defineComponent } = require( 'vue' );
module.exports = defineComponent( {
    computed: {
        fooOrBarMsg: function () {
            // Note that this returns a Message object, not a string
            return mw.message( this.foo ? 'foo-msg' : 'bar-msg' )
                .params( [ this.baz, this.quux ] );
        }
    }
} );
</script>

Other topics

[edit]

Testing

[edit]

You should write tests! The current recommendation is to use Jest.

See the Vue.js testing guide for more information about how to test Vue components in a MediaWiki environment.

Error reporting

[edit]

Client side errors can be monitored in Logstash. Dedicated documentation forthcoming in T248884.

TypeScript

[edit]

(coming soon)

IE 11

[edit]

As MediaWiki is using Vue 3, any features built with it will not work with IE11. See Compatibility/IE11 for more information.

Projects using Vue.js

[edit]
Project Author Status Components Docs Source Code Notes
CodexExample Design Systems Team Experimental Codex Extension:CodexExample GitLab Example MediaWiki extension that uses Codex components (Vue and CSS-only), design tokens, and icons
Commons MediaSearch Structured Data Team In Production Codex, some Structured Data custom components Extension:MediaSearch Gerrit The development of this feature was written about in a Wikimedia Tech Blog post. Provides a fallback no-js interface using PHP and Mustache; the Vue-based UI replaces this when JS initializes. Most of the custom base components were migrated to Codex in June 2023, but some remain.
Content Translation 3 (CX3) Language Team In Production MW components (see here for source) Extension:ContentTranslationContent translation/Section translation Gerrit One of the first MediaWiki features to adopt Vue.js. This feature is developed as a stand-alone application outside of MediaWiki and bundled using Vite. More info about the development of CX3 can be found here.
Desktop Improvements Typeahead Search Web Team In Production Codex Desktop Improvements Part of Vector 2022 The initial pilot project for Vue.js usage in MediaWiki. This project was originally developed using WVUI (Web Team Vue 2 component library), but has since been migrated to Codex. The TypeaheadSearch component itself lives within the Codex library and can be used elsewhere.
Growth Experiments Mentor Dashboard Growth Team In production Codex, custom components Extension:GrowthExperiments See T297763
Growth Experiments New Impact module Growth Team In production Codex, custom components Extension:GrowthExperiments See T222310
NearbyPages Extension Web Team In Development Codex Extension:NearbyPages Gerrit NearbyPages was originally part of MobileFrontend extension; it was migrated to Vue & Codex as part of its refactor into a stand-alone extension that can be used more widely.
PageTriage Moderator Tools Team In Production Codex Extension:PageTriage Gerrit
QuickSurveys Extension Web Team In Production Codex Extension:QuickSurveys Gerrit Originally developed using WVUI, QuickSurveys has been migrated to Codex as of July 2022.
ReadingLists extension Web Team In Production Codex Extension:ReadingLists Gerrit
RelatedArticles Extension Web Team Bootstrap version in production; Vue.js port in progress Codex port in progress Extension:RelatedArticles Gerrit Deployment of Vue.js version is currently blocked until some questions around performance/SSR can be resolved.
SearchVue Extension Structured Data Team In Development Minimal Codex use (for one icon), custom components otherwise Extension:SearchVue Gerrit Experimental project. Will adapt some of the UI elements from MediaSearch to enhance the standard search page.
Toolhub Wikimedia Technical Engagement In Production Custom components meta:Toolhub Gerrit Developed outside of MediaWiki using Vue-CLI. Vue 2 application.
VueTest Design Systems Team Experimental Vue 3 (compat. mode)/Codex Extension:VueTest Gerrit Sandbox and testing ground for Vue.js code in MediaWiki.
Wikibase Tainted References WMDE Gerrit
Wikibase Termbox WMDE In Production (mobile only) Wikit WMDE developed a stand-alone SSR service to support this feature.
Wikidata Bridge WMDE In Production Wikit Wikidata bridge Gerrit Currently on selected wikis only
Wikidata Query Builder WMDE In Production Wikit Query Builder Gerrit
WikiLambda Abstract Wikipedia Team In Development Codex Extension:WikiLambda Gerrit DST collaborated with Abstract Wikipedia team to alpha-test some Codex components in this project.
WikiLove Unmaintained In production Codex (minimally) Extension:WikiLove Gerrit A minimal port to Vue was done by Jon Robson, but more work remains (e.g. using the Codex Dialog component)
WikiSearchFront Robis Koopmans (Wikibase Soltuions) Released MW 1.31+; ships with its own vue.min.js Extension:WikiSearchFront Github Front-end for the WikiSearch extension

Resources

[edit]

Most important

[edit]

Additional

[edit]

Training vendors

[edit]

Further reading

[edit]

From around MediaWiki

[edit]

External organizations choosing Vue

[edit]

GitLab

[edit]

Subpages

[edit]