Experiments in Vue 3 Reactivity to build Custom Elements

Andrew Beng

github.com/andrewbeng89
andrewbeng89.me
andrew@factorial.io

Reactivity
 

Custom Elements

Experiment

Vue 3.x Reactivity TL;DR

  • The "magic" behind Vue
  • 2.x Vue.observable() now 3.x reactive()
  • Uses ES6 Proxy (easy effects + optional read-only), no IE support
  • Standalone package
import { reactive, effect } from
	"https://unpkg.com/@vue/reactivity?module";

const state = reactive({
  count: 0
});

effect(() => {
  console.log(state.count);
  // 0
  // 1
});

const add = () => state.count++;

add();
... published as a package that can be used standalone

Reactivity
Custom Elements

Experiment

Custom Elements

 

Custom Elements - TL;DR

  • Core Web Components feature
  • Create new HTML tags
  • Extend existing (built-in or custom) tags
  • Creating reusable components with vanilla JS/HTML/CSS

Custom Elements
- in the Wild

Custom Elements - Pros

  • Standards based
  • Interoperability

Custom Elements - Cons

  • "Boilerplate" code (vanilla)
  • MANY libraries/frameworks
  • "Web Components are not Vue / React / <insert any framework> components" 

Custom Elements

- Best Practices

  • Create a shadow root to encapsulate styles
  • Aim to keep primitive data attributes and properties in sync, reflecting from property to attribute, and vice versa.
  • ... and many others

Reactivity
Custom Elements

 

Experiment

Evan You's vue-lit

Experiment goals

  • Based on vue-lit (props + lifecycle hooks)
  • Props validation
  • Add a <slot> interface
  • Testable
export default ({ name, setup, props }) => {
  customElements.define(
    name,
    class extends HTMLElement {
      static get observedAttributes() {
        // Return a list of observed attribute names
      }

      constructor() {
        // 1. Scaffold reactive props
        // 2. Scaffold slots as reactive object
        // 3. Apply effect to render the template + run hooks
      }

      connectedCallback() {
        // 1. Run beforeMount hook
        // 2. Render template + invoke setup()
        // 3. Run mounted hook
        // 4. Bind template slots to reactive object
        // 5. Validate props
      }

      disconnectedCallback() {
        // Run unmounted hook
      }

      attributeChangedCallback() {
        // Parse, validate and update reactive props
      }
    }
  );
}
import { defineComponent, reactive, html }
	from "https://unpkg.com/vue-uhtml?module";

defineComponent({
  name: "my-component",
  setup: () => {
    const state = reactive({
      text: "hello"
    });
    
    const onInput = (e) => {
      state.text = e.target.value;
    };

    return () => html`
      <p>
        <input value=${state.text} oninput=${onInput} />
        <span>${state.text}</span>
      </p>
    `;
  },
});
<my-component></my-component>

Testing

  • Test defined Custom Elements
  • @web/test-runner
  • events, properties, lifecycle hooks
  • Gotha: closed vs open shadowRoot

Take-aways

  • Standalone reactivity is awesome
  • Custom Elements aren't scary
  • Get out there and experiment!
Made with Slides.com