Page MenuHomePhabricator

Create a corresponding set of "lite" components suitable for progressive enhancement with petite-vue
Closed, DeclinedPublic

Description

This idea was inspired by some of the discussion around T286835 (and related tasks like T289718).

As WVUI currently stands, we don't have a great way to deliver Vue.js-based UI components in performance-critical contexts, which has required the Web team to come up with various work-arounds (e.g. only load the WVUI Typeadhead Search component after the user interacts with the server-rendered search input in the article chrome). Many other reader-facing features (sticky header, related articles, etc) will offer similar challenges.

Our new shared library should offer better support for performance-critical / progressively-enhanced use cases where loading all of Vue.js and the full library is not practical. One way to do that would be to provide a "lite" subset of the UI that is powered by petite-vue.

Petite-vue is a relatively new distribution of Vue.js (it is built around Vue 3's reactivity module) that is optimized for progressive enhancement. The full library is around 6KB minified and gzipped. This is small enough that we could likely deliver it to the user immediately instead of relying on workarounds to delay loading, which would hopefully simplify things for many of us. Petite-vue "components" would re-use existing DOM elements, but a most of the template, logic, and styles for the "real" components could be re-used.

We don't necessarily need a "lite" version of every component, but we'd want to support many of the features the Web team is currently working on. In particular, I think we could re-implement the entire TypeaheadSearch element as a set of petite-vue components that progressively enhance – rather than replace – the server-rendered search markup on article pages. (Related: T291526)

The library's component roadmap could list which components need to exist in which versions, and the overall documentation for the shared library could provide developers with guidelines for when to use which type of component.

To help ensure consistency, all code for both sets of components should probably live in the same repository, even though we will want to provide different builds.

Example: "wvui-lite" button

Here's an example of how the WvuiButton could be re-implemented in petite-vue. In this example, the assumption is that markup is generated in PHP (probably using Mustache templates or similar) and then JS is delivered via ResourceLoader (assuming the client has JS enabled). This approach should make it straightforward to design a UI that is usable and complete without JS (or before it is finished loading).

Button.js
/**
 * Button component. Accepts a user-provided function which is bound to the
 * click handler.
 */
export default function Button ( props ) {
	return {
		/**
		 * @type string
		 */
		action: props.action,

		/**
		 * @type string
		 */
		type: props.type,

		/**
		 * The original WVUI button emits an event to the parent Vue component, but
		 * it probably makes more sense to just pass down the function we want to use
		 * in this scenario
		 * @type Function
		 */
		onClick: props.onClick,

		/**
		 * Computed property example
		 * @return {Object.<string, boolean>}
		 */
		get rootClasses () {
			return {
				'wvui-button--action-default': this.action === 'default',
				'wvui-button--action-progressive': this.action === 'progressive',
				'wvui-button--action-destructive': this.action === 'destructive',
				'wvui-button--type-primary': this.type === 'primary',
				'wvui-button--type-normal': this.type === 'normal',
				'wvui-button--type-quiet': this.type === 'quiet',
				'wvui-button--framed': this.type !== 'quiet' 
			}
		}
	}
}
template.html
<div id="app" v-scope>
    <!-- v-effect will execute the inline statement whenever it changes -->
    <div v-effect="$el.textContent = count">Enable JS to see the count</div>

    <!--
        Button component uses the existing DOM element as its template.
        It's also possible to provide the template as a string or as
        an external <template> element. We could even setup a mustache
        partial that accepted parameters if we wanted to programmatically
        generate these buttons in PHP.
    -->
 
    <!-- props for the first button are set up in JS -->
    <button 
        v-scope="Button( incrementButton )" 
        class="wvui-button" 
        :class="rootClasses" 
        @click="onClick()"
    >
        Increase the count
    </button>

    <!-- props for the second button are provided inline in the template -->
    <button 
        v-scope="Button( { action: 'destructive', onClick: decrement } )" 
        class="wvui-button" 
        :class="rootClasses" 
        @click="onClick()"
    >
        Decrease the count
    </button>
main.js
import { createApp } from 'petite-vue';
import { Button } from './lib';

createApp( {
	// exposed to all expressions
	count: 0,

	increment () {
		this.count++;
	},

	decrement () {
		this.count--;
	},

	// Similar to component declaration; make the button "component"
	// available
	Button,

	// Props for individual component instances can be provided inline
	// inside a v-scope expression, or a property can be declared here:
	incrementButton: {
		action: 'progressive',
		onClick() {
			this.increment();
		}
	}
} ).mount();

Event Timeline

this sounds pretty cool. I could see petite Vue being useful for server-side rendered HTML, however I think Vue.js would still be important for more rich experiences e.g. MediaSearch / Special:Nearby. Is there a problem with both libraries, or can Petite Vue preload some of the code Vue needs?

I assume WVUI's successor can be built in a way that it supports both Vue.js and Petite Vue or would there need to be Lite components and non-Lite components?

this sounds pretty cool. I could see petite Vue being useful for server-side rendered HTML, however I think Vue.js would still be important for more rich experiences e.g. MediaSearch / Special:Nearby. Is there a problem with both libraries, or can Petite Vue preload some of the code Vue needs?

I think that we'd continue to use "full" Vue for Special pages (and any sort of dashboard / dynamic UI where we expect the user to stick around for a while). As far as pre-loading code, Petite-Vue is built around @vue/reactivity. It *might* be possible to provide our own builds of Petite- and "regular" Vue that externalize that module so we only have to load it once, but I could also see that leading to some real headaches. Worth investigating a bit further anyway.

I assume WVUI's successor can be built in a way that it supports both Vue.js and Petite Vue or would there need to be Lite components and non-Lite components?

We'd probably need to build two sets of components (at least for anything we wanted in the "lite" version). But I think a lot of the code could be shared. For example, most of the template code could be re-used; component logic would be similar but components that are built out of lots of other components would need to be approached differently. It shouldn't be to hard to make something that matches the ultimate markup & styles 100% though.

The work being done in T321351: [EPIC] Add CSS-only components will supersede this proposal.