Vue vs Vanilla
Components for the Web

Johannes Kissel & Michael Müller

Accento Logo
Johannes Kissel

Johannes Kissel

Software Developer
at Disy Logo
in Karlsruhe

Enthusiastic about …

  • 🔗 The Open Web
  • 🚲 Cycling
  • 🎶 Singing
Michael Müller

Michael Müller

Full Stack Developer
at Echobot Logo
in Karlsruhe

Enthusiastic about …

  • 💻 Web Development
  • 🎶 Singing
  • 🍳 Cooking

Cadenza is big …

Cadenza sreenshot showing a map

… and it's legacy, but …

… not that kind of legacy …

Dung Heap
Dung heap, © Superbass / CC-BY-SA-3.0 (via Wikimedia Commons)

… or that kind:

Jenga Tower
Jenga tower, © Marco Verch / CC-BY-2.0 (via Flickr)

It's vivid and
constantly improved and extended










Frankfurt main station, © Urmelbeauftragter / CC-BY-SA-3.0 (via Wikimedia Commons)

How to be future proof?

Refactoring on the go demands
maximum interoperability


                            const ELEMENT_NAME = 'd-toggle-button';

                            class ToggleButton extends HTMLElement {
                              connectedCallback () {
                                this.classList.add(ELEMENT_NAME);
                                this.setAttribute('role', 'button');
                                this.addEventListener('click', () => {
                                  this.pressed = !this.pressed;
                                  this.dispatchEvent(new CustomEvent('toggle', {bubbles: true}));
                                });
                              }
                              set pressed (value) {
                                this.setAttribute('aria-pressed', value);
                              }
                              get pressed () {
                                return this.getAttribute('aria-pressed') === 'true';
                              }
                            }

                            customElements.define(ELEMENT_NAME, ToggleButton);
                        

jsfiddle.net/w15c8mpx/

Usage with Plain DOM


                            const button = document.createElement('d-toggle-button');
                            button.textContent = 'Button';
                            button.pressed = true;
                            button.addEventListener('toggle', () => {
                              console.log(button.pressed);
                            });
                            document.body.append(button);
                        

Usage with Constructor


                            import ToggleButton from 'ui/toggle-button';
                            // or
                            const ToggleButton = customElements.get('d-toggle-button');

                            // Constructor
                            const button = new ToggleButton();
                            …
                        

Usage with jQuery


                            const $button = $('<d-toggle-button>')
                              .text('Button')
                              .prop('pressed', true)
                              .on('toggle', () => console.log($button.prop('pressed')))
                              .appendTo(document.body);
                        

Usage with Vue


                            new Vue({
                              el: '#app',
                              template: `<d-toggle-button
                                           :pressed.prop="pressed"
                                           @toggle="onToggle">Button</d-toggle-button>`,
                              data: { pressed: true },
                              methods: {
                                onToggle: event => console.log(event.target.pressed)
                              }
                            });
                        

Web Components






The Holy Grail

Web Components






The Holy Grail

A solid foundation
with the right level of abstraction

Railroad ties, © Störfix / CC-BY-SA-3.0 (via Wikimedia Commons)

A software component is a
unit of composition with contractually specified interfaces and explicit context dependencies only.

The effect is HUGE!

Arrow pointing from the Grunt logo to the Webpack logo, indicating the transition from one tool to the other.

Button

UI components have a name and can be reused.

Design Tools: Style Guide

Screenshot of the Disy style guide

(Zeplin)

Design Tools: Component library

Screenshot of the Disy component library

(Catalog)

Web Components

  • Custom Elements
  • Shadow DOM
  • HTML Templates

Custom elements


                            class MyComponent extends HTMLElement {
                              …
                            }

                            customElements.define('d-my-component', MyComponent);
                        

(Google primer on custom elements)

Custom Elements: Extending Built-ins


                            class MyButton extends HTMLButtonElement {
                              …
                            }

                            customElements.define('d-my-button', MyComponent, {extends: 'button'});

                            // Usage:
                            document.createElement('button', {is: 'd-my-button'});
                            // or
                            // new MyButton();
                            // <button is="d-my-button">My Button</button>
                        

Custom Elements: Browser Support

Data on support for the custom-elementsv1 feature across the major browsers from caniuse.com

Custom Elements: Polyfilling


                            <script>
                              if (window.customElements) {
                                // Make the browser work with transpiled custom elements.
                                document.write('<script src="native-shim.js"></script>');
                              } else {
                                // Polyfill custom elements.
                                document.write('<script src="custom-elements.min.js"></script>');
                              }
                            </script>
                        

Polyfill for Custom Elements

⇒ We look forward to extend built-in elements in the future.

Custom Elements: Lifecycle Callbacks


                            class MyComponent extends HTMLElement {
                              constructor () {
                                // Called when the element is created or upgraded.
                                super(); // Don't forget to call super().
                                …
                              }
                              connectedCallback () {
                                // Called when the element is connected to the DOM.
                              }
                              disconnectedCallback () {
                                // Complement to the connectedCallback()
                              }
                            }
                        

Custom Elements: Attributes


                            class MyComponent extends HTMLElement {
                              static get observedAttributes() {
                                return ['some-attribute']; // Return attribute names to observe.
                              }
                              attributeChangedCallback (name, oldValue, newValue) {
                                // Called when the observed attributes' values change.
                              }
                            }
                        

Custom Elements: Properties


                            class ToggleButton extends HTMLElement {
                              connectedCallback () {
                                this.addEventListener('click', () => {
                                  this.pressed = !this.pressed;
                                  this.dispatchEvent(new CustomEvent('toggle', {bubbles: true}));
                                });
                              }
                              set pressed (value) {
                                this.setAttribute('aria-pressed', value);
                              }
                              get pressed () {
                                return this.getAttribute('aria-pressed') === 'true';
                              }
                            }
                        

Shadow DOM


                            class Tabs extends HTMLElement {
                              constructor() {
                                super();

                                const shadowRoot = this.attachShadow({mode: 'open'});
                                shadowRoot.innerHTML = `
                                  <style>#tabs { border-bottom: 1px solid; }</style>
                                  <div id="tabs">
                                    <slot name="tabs" />
                                  </div>
                                  <div id="panels">
                                    <slot />
                                  </div>
                                `;
                              }
                            }
                        
  • Scoped Styles
  • Declarative Composition

jsfiddle.net/09dyg34p/1/ (Google primer)

Shadow DOM: Browser Support

Data on support for the shadowdomv1 feature across the major browsers from caniuse.com

Polyfill for the Shadow DOM

Shadow DOM: "Challenges"

  • Polyfills are "shady" and horribly slow.
  • Scoped styles require native CSS custom
    properties to be really useful.
  • No server-side rendering (although people try)

⇒ We do not use the Shadow DOM and do not recommend it.

HTML Templates


                            <ul id="todoList"></ul>
                            <template id="todoTemplate">
                              <li>
                                <label>
                                  <input type="checkbox"> <span class="text"></span>
                                </label>
                              </li>
                            </template>
                        

                            const todos = document.querySelector('#todoList');
                            const template = document.querySelector('#todoTemplate');
                            template.content.querySelector('.text').textContent = 'Give a talk';
                            template.content.querySelector('input').checked = true;
                            todos.append(document.importNode(template.content, true));
                        

jsfiddle.net/teg37rao/

HTML Templates: Browser Support

Data on support for the template feature across the major browsers from caniuse.com

Polyfill for HTML Templates

⇒ HTML Templates work nicely, but we don't use them, yet.

And did I mention…

  • 🔋 Batteries included
  • 🎓 Flat learning curve
  • 🛠️ Great devtools
  • ️ You can use <x> with Web Components
  • 📈 Big companies do commitments

Bottom line

  • We have three years of experience with Custom Elements.
  • Recently, we replaced our last RichFaces component.
  • We benefit a lot and we'll move on into that direction.

Keep calm and #UseThePlatform

Departing train, © Xenotron / CC BY 4.0 (via Wikimedia Commons)

  • ~100 Servers
  • 4 Developers
  • every project has one responsible person
  • goal is uniform technology landscape
  • Name: DATACARE
  • Cleaning and enrichment of company data
  • Prototype ready for DMEXCO exhibition

There was only one problem…

AngularJS

Analyzing JS frameworks

  • Angular is too heavy weighted
  • WTF is a Web Component?!
  • I knew Vue.js from an internal project

The technologies we agreed on

  • Vue Logo
  • TypeScript Logo
  • Element UI Logo

What is Vue.js?

  • pronounced like "view"
  • created in 2014 by Evan You
  • started as a side project when he worked for Google*
  • component centric
  • jquery.min.js: 84,8 KB | vue.min.js: 85,6 KB
*this "side project" has now more Github stars than React :)
Todo example

jsfiddle.net/zfoexhrq/2

Project setup

Option #1

Option #2


                                    npm install -g @vue/cli
                                
Vue CLI

It get's better…

Single File Components

  • all the parts that belong together in one file
  • need some time getting used to but make total sense
  • SFCs are opt-in
  • JSX is also possible
  • if your SFC gets too big: create a subcomponent!

HelloWorld.vue


                            

                            

                            
                        

codesandbox.io/s/vue-template-eikjx

main.js


                            import Vue from "vue";
                            import HelloWorld from "./HelloWorld.vue";

                            Vue.component("hello-world", HelloWorld);

                            new Vue({
                              el: "#app",
                              template: ""
                            });
                            

codesandbox.io/s/vue-template-eikjx

How to structure a Vue app?

What I ️ about Vue

  • ☯️ Simplicity
  • 📖 Vue documentation
  • 🚀 Vue ecosystem
    • vue-router
    • vue-i18n
    • Nuxt.js

Bottom line

  • The prototype got finished in time.
  • After 6 months my boss was finally convinced.
  • New Vue projects are spreading.

Keep calm and #becomeaddicted

© Ryoji Iwata via unsplash.com

How to convince YOUR boss?

#1

Build something with it

#2

Collect arguments

#3

Sit back and relax

Questions?