Jump to content

Manual:Coding conventions/Vue: Difference between revisions

shortcut: CC/V
From mediawiki.org
Content deleted Content added
Marked this version for translation
→‎Style: add docs link for "Object syntax"
(4 intermediate revisions by 3 users not shown)
Line 50: Line 50:
<translate><!--T:15--> Vue's [<tvar name=url>https://vuejs.org/guide/scaling-up/sfc.html</tvar> single-file components] format (<tvar name=1><code>.vue</code></tvar>) should be used wherever possible.</translate>
<translate><!--T:15--> Vue's [<tvar name=url>https://vuejs.org/guide/scaling-up/sfc.html</tvar> single-file components] format (<tvar name=1><code>.vue</code></tvar>) should be used wherever possible.</translate>
<translate><!--T:16--> This allows templates, logic, and (optionally) styles for a given component to live together in one file.</translate>
<translate><!--T:16--> This allows templates, logic, and (optionally) styles for a given component to live together in one file.</translate>
{{ll|ResourceLoader}} supports on-the-fly compilation of <code>.vue</code> files ({{ll|Vue.js#Using Vue in MediaWiki|see here for more info about using Vue with ResourceLoader}}).
<translate><tvar name=1>{{ll|ResourceLoader}}</tvar> supports on-the-fly compilation of <tvar name=2><code>.vue</code></tvar> files (see {{<tvar name=3>ll|Vue.js#Using Vue in MediaWiki</tvar>|Vue.js#Using Vue in MediaWiki}} for more info about using Vue with ResourceLoader).</translate>
<translate><!--T:17--> This allows developers to write <tvar name=1><code>.vue</code></tvar> files without needing to rely on any new build tools (Rollup, Webpack, etc), while still benefiting from the various optimizations that ResourceLoader provides for front-end code (RTL styling via CSS Janus, JS minification, etc).</translate>
<translate><!--T:17--> This allows developers to write <tvar name=1><code>.vue</code></tvar> files without needing to rely on any new build tools (Rollup, Webpack, etc), while still benefiting from the various optimizations that ResourceLoader provides for front-end code (RTL styling via CSS Janus, JS minification, etc).</translate>


Line 61: Line 61:
=== General Structure === <!--T:22-->
=== General Structure === <!--T:22-->
</translate>
</translate>
Single-file components are broken into three sections: {{tag|template|open}}, {{tag|script|open}}, and {{tag|style|open}}; components should follow this order, with the {{tag|style|open}} block being optional.
<translate>Single-file components are broken into three sections: <tvar name=1>{{tag|template|open}}</tvar>, <tvar name=2>{{tag|script|open}}</tvar>, and <tvar name=3>{{tag|style|open}}</tvar>; components should follow this order, with the <tvar name=3>{{tag|style|open}}</tvar> block being optional.</translate>
Each component file should be listed individually under the <code>packageFiles</code> property in the appropriate module definition in <code>extension.json</code>.
<translate>Each component file should be listed individually under the <tvar name=1><code>packageFiles</code></tvar> property in the appropriate module definition in <tvar name=2><code>extension.json</code></tvar>.</translate>
Make sure that the module definition also includes the <code>vue</code> module as a dependency.
<translate>Make sure that the module definition also includes the <tvar name=1><code>vue</code></tvar> module as a dependency.</translate>


<translate>
=== Template ===
=== Template ===
</translate>
* '''Component tags must not be self-closing''': this is a departure from [https://vuejs.org/style-guide/rules-strongly-recommended.html#self-closing-components Vue's style guide recommendations] based on current limitations in ResourceLoader. Regardless of whether a component uses slots, it should have a closing tag.
* '''<translate>Component tags must not be self-closing</translate>''' - <translate>this is a departure from [<tvar name=url>https://vuejs.org/style-guide/rules-strongly-recommended.html#self-closing-components</tvar> Vue's style guide recommendations] based on current limitations in ResourceLoader.</translate> <translate>Regardless of whether a component uses slots, it should have a closing tag.</translate>


{|style="text-align: left;"
{|style="text-align: left;"
Line 128: Line 130:
**:
**:


==== Script ====
=== Script ===
* '''Single-file components delivered via ResourceLoader should follow the CommonJS module format''' and should use [[ ResourceLoader/Package_files |ResourceLoader's PackageFiles feature]]. This means that each component file should include a <code>module.exports</code> statement, and should import other code using <code>require():</code>
* '''Single-file components delivered via ResourceLoader should follow the CommonJS module format''' and should use [[ ResourceLoader/Package_files |ResourceLoader's PackageFiles feature]]. This means that each component file should include a <code>module.exports</code> statement, and should import other code using <code>require():</code>
*:<syntaxhighlight lang="javascript">
*:<syntaxhighlight lang="javascript">
Line 148: Line 150:
* '''[https://vuejs.org/style-guide/rules-use-with-caution.html#implicit-parent-child-communication Avoid implicit parent-child communication]''' and the mutation of received props within a child component. Prefer the "props down / events up" approach
* '''[https://vuejs.org/style-guide/rules-use-with-caution.html#implicit-parent-child-communication Avoid implicit parent-child communication]''' and the mutation of received props within a child component. Prefer the "props down / events up" approach


==== Style ====
==== Options API vs. Composition API ====
Vue provides two APIs for writing components: the [https://vuejs.org/guide/introduction.html#options-api Options API] and the [https://vuejs.org/guide/introduction.html#composition-api Composition API]. Either is allowed; consult the Vue documentation on [https://vuejs.org/guide/introduction.html#which-to-choose choosing a component API]. Note that [[wmdoc:codex/main/components/overview.html|Codex component demos]] are mostly built with the Composition API, so using it may make copying code samples easier. Additionally, Codex exports several [[wmdoc:codex/main/composables/overview.html|composables]] that may be useful in feature code that uses the Composition API.

=== Style ===
* MediaWiki [[Manual:Coding_conventions/CSS|CSS and LESS conventions]] apply, styles are linted with [https://github.com/wikimedia/stylelint-config-wikimedia/ custom Wikimedia stylelint config]
* MediaWiki [[Manual:Coding_conventions/CSS|CSS and LESS conventions]] apply, styles are linted with [https://github.com/wikimedia/stylelint-config-wikimedia/ custom Wikimedia stylelint config]
* Since each component should contain a single top-level component, styles should be nested under a single selector (if using LESS)
* Since each component should contain a single top-level component, styles should be nested under a single selector (if using LESS)
* Conditional CSS classes should be used for dynamic styles. Object syntax is preferred for computed properties that are bound to class attributes.
* Conditional CSS classes should be used for dynamic styles. [https://vuejs.org/guide/essentials/class-and-style.html#binding-to-arrays Object syntax] is preferred for computed properties that are bound to class attributes.
*[https://vuejs.org/guide/built-ins/transition.html Vue transition] names should follow the same pattern as CSS class names (e.g. including an extension-specific prefix)
*[https://vuejs.org/guide/built-ins/transition.html Vue transition] names should follow the same pattern as CSS class names (e.g. including an extension-specific prefix)



Revision as of 04:11, 9 July 2024

This page describes coding conventions for Vue in the MediaWiki codebase. See also the JavaScript and CSS/LESS conventions which apply to such code within Vue files.

Linting

We use ESLint as our code quality tool. The custom config for Wikimedia (eslint-config-wikimedia) contains both general and MediaWiki-specific rules for Vue code.

Making ESLint recognize component definitions

You must use the defineComponent() function from Vue for your component definition for it to be recognized as such by ESLint (see example below). If ESLint detects that you're not using this, it will warn you and tell you to add it, but it won't be able to interpret your code as Vue code until you do so.

<script>
const { defineComponent } = require( 'vue' );
const AnotherComponent = require( './AnotherComponent.vue' );

module.exports = exports = defineComponent( {
    name: 'MyComponent',
    components: { AnotherComponent },
    ...
} );
</script>


Setting up ESLint

If you are already using the wikimedia/client or wikimedia/client/es6 preset from the eslint-config-wikimedia package in your .eslintrc.json, lint rules for Vue are automatically applied to .vue files. Starting with version 0.23.0 of eslint-config-wikimedia, the ES5 rules default to Vue 2, and the ES6 rules default to Vue 3. New code should be written using Vue 3 (and consequently, using ES6); the ES5+Vue2 rules are only meant for legacy code.

Naming

Single-file Components

Vue's single-file components format (.vue) should be used wherever possible. This allows templates, logic, and (optionally) styles for a given component to live together in one file. ResourceLoader supports on-the-fly compilation of .vue files (see Vue.js#Using Vue in MediaWiki for more info about using Vue with ResourceLoader). This allows developers to write .vue files without needing to rely on any new build tools (Rollup, Webpack, etc), while still benefiting from the various optimizations that ResourceLoader provides for front-end code (RTL styling via CSS Janus, JS minification, etc).

Where possible, Vue code should follow the Vue community's style guide. In particular, all recommendations in "Priority A: Essential" should be followed at all times. Any MediaWiki-specific exceptions will be called out below. If ESLint is set up correctly, it will enforce all rules from the Vue style guide (Priority A, B and C) as well as MediaWiki-specific rules and exceptions.

General Structure

Single-file components are broken into three sections: ‎<template>, ‎<script>, and ‎<style>; components should follow this order, with the ‎<style> block being optional. Each component file should be listed individually under the packageFiles property in the appropriate module definition in extension.json. Make sure that the module definition also includes the vue module as a dependency.

Template

  • Component tags must not be self-closing - this is a departure from Vue's style guide recommendations based on current limitations in ResourceLoader. Regardless of whether a component uses slots, it should have a closing tag.
Correct: Wrong (self-closing components):
<slot-based-component>
    <h1>Hello world</h1>
</slot-based-component>

<props-component
  :foo="bar"
  :baz="quux"
  @click="doSomething"
></props-component>

<basic-component></basic-component>
<props-component
  :foo="bar"
  :baz="quux"
  @click="doSomething"
/>

<basic-component />
Correct (kebab-case): Wrong (PascalCase):
<some-component :foo="bar"></some-component>
<SomeComponent :foo="bar"></SomeComponent>
  • Use the directive short-hands (:foo instead of v-bind:foo, @click instead of v-on:click).
  • Elements with multiple attributes should break them out onto separate lines
  • Component templates should only include simple expressions; for anything more complex, define a computed property or method in the ‎<script> section instead.
  • Message strings in templates must be internationalized just like in standard JS or PHP UI development. See the documentation on internationalization in Vue for more information.
  • Use v-html sparingly and carefully, because it can lead to XSS vulnerabilities if used incorrectly. If you must use v-html, carefully audit the code that generates the HTML to ensure that all untrusted input is escaped.
    • For parsed i18n messages (mw.message( 'foo' ).parse()), using v-html is not necessary in most cases. Use v-i18n-html instead, if possible.
    • Because v-html is discouraged, using it causes an ESLint error. If you must use it, add <!-- eslint-disable-next-line vue/no-v-html --> on the line above the tag that uses v-html to dismiss the ESLint warning.
    • Before adding this override, double-check your code to ensure your usage of v-html is safe.

Script

  • Single-file components delivered via ResourceLoader should follow the CommonJS module format and should use ResourceLoader's PackageFiles feature. This means that each component file should include a module.exports statement, and should import other code using require():
    const { defineComponent } = require( 'vue' );
    const OtherComponent = require( './OtherComponent.js' );
    
    module.exports = exports = defineComponent( {
        name: 'MyComponent',
        components: {
            OtherComponent
        }
        
        // … etc.
    } );
    
  • Component options should be specified in the order defined here: https://github.com/wikimedia/eslint-config-wikimedia/blob/master/vue-common.json. Generally this means: name, components, mixins, props, emits, setup, data, computed properties, methods, watchers, lifecycle hooks, and finally render functions (in the rare situations where those need to be defined manually).
  • Render functions should be avoided outside of special cases; HTML-style templates are preferred instead. The vue module provided in MediaWiki core is the full version, which includes the template compiler.
  • Use prop definitions and consider specifying defaults or validating data where necessary. Boolean props should default to false.
  • Avoid implicit parent-child communication and the mutation of received props within a child component. Prefer the "props down / events up" approach

Options API vs. Composition API

Vue provides two APIs for writing components: the Options API and the Composition API. Either is allowed; consult the Vue documentation on choosing a component API. Note that Codex component demos are mostly built with the Composition API, so using it may make copying code samples easier. Additionally, Codex exports several composables that may be useful in feature code that uses the Composition API.

Style

  • MediaWiki CSS and LESS conventions apply, styles are linted with custom Wikimedia stylelint config
  • Since each component should contain a single top-level component, styles should be nested under a single selector (if using LESS)
  • Conditional CSS classes should be used for dynamic styles. Object syntax is preferred for computed properties that are bound to class attributes.
  • Vue transition names should follow the same pattern as CSS class names (e.g. including an extension-specific prefix)

Progressive Enhancement

TBD: define a no-JS fallback for most features you build this way.