Web Components

Codecamp_Timisoara

Oct 19, 2019

for everyone

Adrian Fâciu

Web Components

@adrianfaciu

@adrianfaciu

Some problems

@adrianfaciu

Creating components

@adrianfaciu

Datepicker
Typeahead

Datepicker

Grid

Typeahead

@adrianfaciu

@adrianfaciu

Component libraries

@adrianfaciu

@adrianfaciu

@adrianfaciu

Can we do something about this?

@adrianfaciu

Enter Web Components

@adrianfaciu

3 Browser APIs

  • Custom elements

  • Shadow DOM

  • HTML templates

@adrianfaciu

Custom elements

Define custom elements and their behavior

@adrianfaciu

Custom elements

CustomElementRegistry - define method

  • element name
  • A class object
  • built-in element (optionally)

@adrianfaciu

Custom elements

customElements.define(
   'word-count',
    WordCount,
    { extends: 'p' },
);

@adrianfaciu

Shadow DOM

Attach an encapsulated "shadow" DOM tree to an element

@adrianfaciu

Shadow DOM

let shadow = elementRef.attachShadow({mode: 'open'});

let shadow = elementRef.attachShadow({mode: 'closed'});

@adrianfaciu

Shadow DOM

@adrianfaciu

HTML templates

The <template> and <slot> elements

@adrianfaciu

Templates

<template id="my-paragraph">
  <p>My paragraph</p>
</template>

@adrianfaciu

Templates

let template = document.getElementById('my-paragraph');
let templateContent = template.content;

document.body.appendChild(templateContent);

@adrianfaciu

Slots

<template>
  <p>
    <slot name="my-text">My default text</slot>
  </p>
</template>

@adrianfaciu

Slots

<my-paragraph>
  <span slot="my-text">
    Let's have some different text!
  </span>
</my-paragraph>

Can we use this?

@adrianfaciu

@adrianfaciu

Browser support

caniuse.com

@adrianfaciu

Browser support

caniuse.com

@adrianfaciu

Browser support

caniuse.com

@adrianfaciu

Who is using this?

@adrianfaciu

@adrianfaciu

5% of page loads

chromestatus.com

@adrianfaciu

@adrianfaciu

Creating web components

@adrianfaciu

Browser APIs

class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    // Create a shadow root
    var shadow = this.attachShadow({mode: 'open'});
    
    // write element functionality in here

    ...
  }
}

@adrianfaciu

Browser APIs

customElements.define('popup-info', PopUpInfo);

@adrianfaciu

Browser APIs

<div>
  <popup-info></popup-info>
</div>

@adrianfaciu

Browser APIs

// Create spans
var wrapper = document.createElement('span');
wrapper.setAttribute('class','wrapper');
var icon = document.createElement('span');
icon.setAttribute('class','icon');
icon.setAttribute('tabindex', 0);
var info = document.createElement('span');
info.setAttribute('class','info');

@adrianfaciu

Browser APIs

// Take attribute content and put it inside the span
var text = this.getAttribute('text');
info.textContent = text;

@adrianfaciu

Browser APIs

// Insert icon
var imgUrl;
if(this.hasAttribute('img')) {
  imgUrl = this.getAttribute('img');
} else {
  imgUrl = 'img/default.png';
}
var img = document.createElement('img');
img.src = imgUrl;
icon.appendChild(img);

@adrianfaciu

Browser APIs

// Create some CSS to apply to the shadow dom
var style = document.createElement('style');

style.textContent = '.wrapper {' +
// CSS truncated for brevity

@adrianfaciu

Browser APIs

// attach the created elements to the shadow dom

shadow.appendChild(style);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);

@adrianfaciu

Browser APIs

  • no lifecycle callbacks

  • no customization

  • no templating engine

@adrianfaciu

Alternatives

@adrianfaciu

LitElement

@adrianfaciu

LitElement

  • fast

  • declarative

  • lightweight

@adrianfaciu

Declarative

 render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }

@adrianfaciu

Lightweight

bundlephobia.com

@adrianfaciu

LitElement

import { LitElement, html, property, customElement }
  from 'lit-element';

@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
  @property() name = 'World';

  render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }
}
<simple-greeting name="Everyone"></simple-greeting>

@adrianfaciu

Stencil

@adrianfaciu

Stencil

  • Virtual DOM

  • Async rendering and reactive data-binding

  • TypeScript & JSX

@adrianfaciu

Virtual DOM

Fast DOM updates without common DOM performance pitfalls

@adrianfaciu

Lazy loading

By default components load asyncronously and can be bundled with related components

@adrianfaciu

Reactive

Updates based on property and state changes

@adrianfaciu

Stencil

import { Component, Prop, h }
  from '@stencil/core';

@Component({
  tag: 'my-first-component',
})
export class MyComponent {
  @Prop() name: string;

  render() {
    return (
      <p>
        My name is {this.name}
      </p>
    );
  }
}

@adrianfaciu

Stencil

<my-first-component name="Max"></my-first-component>

@adrianfaciu

A few other alternatives also

@adrianfaciu

Stencil

LitElement

Browser APIs

@adrianfaciu

It depends

@adrianfaciu

They are not perfect

  • (some) issues when used in React

  • polyfills (IE-Edge)

  • no progressive enhancement

 

@adrianfaciu

Try web components today!

@adrianfaciu

They will not replace the libraries and frameworks that you love!

@adrianfaciu

Meetup

timjs.ro

@adrianfaciu

slides.com/adrianfaciu/webcomponents

Web Components

By Adrian Faciu

Web Components

Talk about Web Components at CodeCamp Timisoara, Oct 19, 2019

  • 651