Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Web Components – das Ende der SPA-Framework-Ära?

Web Components – das Ende der SPA-Framework-Ära?

Web Components – ein Begriff, den wir immer öfter lesen und lesen werden. Dahinter stehen die Technologien Custom Elements, Shadow DOM und HTML Templates. Zusammen ergeben sie ein natives Komponentenmodell für unseren Browser. Es hilft uns, wiederverwendbare UI-Komponenten zu entwickeln, die wir in jeder App benutzen können. Völlig gleich, ob diese Anwendungen mit einem SPA-Framework entwickelt wurden.

Bedeutet das aber auch, dass wir eigentlich gar kein SPA-Framework mehr benötigen, sondern alles mit Web Components entwickeln können? Dieser spannenden Frage geht Manuel Rauber in diesem Webinar nach anhand einer Real-World-Demo-Applikation, Live-Coding und den Vor- und Nachteilen von Web Components.

Manuel Rauber

October 28, 2020
Tweet

More Decks by Manuel Rauber

Other Decks in Programming

Transcript

  1. Web Components
    Das Ende der SPA-Framework-Ära?
    Manuel Rauber
    @ManuelRauber
    Consultant

    View Slide

  2. Manuel Rauber
    Consultant @ Thinktecture AG
    [email protected]
    @manuelrauber
    https://manuel-rauber.com

    View Slide

  3. • The challenge
    • Web Components
    • What are they?
    • How to use them?
    • How to create them?
    Agenda

    View Slide

  4. The challenge

    View Slide

  5. The challenge
    https://dribbble.com/shots/7169781-Homestay-Web-Design https://dribbble.com/shots/7164322-Food-delivery-Landing-page-design

    View Slide

  6. DRY

    View Slide

  7. Components

    View Slide

  8. View Slide

  9. <

    View Slide


  10. <


    <
    <

    <
    <
    • No semantic
    • Intransparent
    • Changes are difficult
    • Framework dependent

    View Slide




  11. <
    <

    <
    <
    <
    +<
    --<
    <
    • Global Scoping
    • Naming conflicts
    • Styling conflicts

    View Slide

  12. <


    <
    <

    <
    <
    • Semantic
    • Local Scoping
    • Configuration
    • Bundle Import

    View Slide

  13. Web Components

    View Slide

  14. Bring a native component model to web instead of having to use frameworks
    Web Components - What are they?
    Custom Element

    Shadow DOM HTML Template

    View Slide

  15. Can I use …?
    IE11 Edge Chrome Android Opera Safari iOS
    Custom
    Elements
    ❌ ✅ ✅ ✅ ✅ ⚠ ⚠
    Shadow
    DOM
    ❌ ✅ ✅ ✅ ✅ ✅ ⚠
    HTML
    Templates
    ❌ ✅ ✅ ✅ ✅ ✅ ✅

    View Slide

  16. Custom Elements

    View Slide

  17. • Create your own HTML tags
    • Lifecycle model (“connected”, “disconnected”, “attribute changed”, “adopted”)
    • Reusable
    • Decouple behavior from the document
    Custom Elements
    <

    View Slide

  18. • ES6 Class
    • Inherit from HTMLElement
    or any other HTML element
    • Need to be defined in a
    CustomElementRegistry
    Custom Elements
    class MyRating extends HTMLElement {
    constructor() {
    super();
    console.log('component constructed');
    }
    connectedCallback() {
    console.log('component added to DOM');
    }
    adoptedCallback() {
    console.log('component was moved in DOM');
    }
    disconnectedCallback() {
    console.log('component removed from DOM');
    }
    attributeChangedCallback(name, oldVal, newVal) {
    console.log('a attribute has been changed');
    }
    }
    window.customElements.define('my-rating', MyRating);
    const myRating =
    document.createElement('my-rating');
    document.body.appendChild(myRating);
    /// this moves the node!
    otherElement.appendChild(myRating);
    otherElement.removeChild(myRating);

    View Slide

  19. • Static getter observedAttributes
    returns all attributes which
    should be observed
    • All those attributes will trigger
    attributeChangedCallback
    upon a change
    • Does not execute on property
    change!
    Custom Elements - Observed Attributes
    class MyRating extends HTMLElement {
    constructor() {
    super();
    }
    static get observedAttributes() {
    return [ 'value' ];
    }
    attributeChangedCallback(name, oldVal, newVal) {
    if (name ===== 'value') {
    this.innerHTML = `Rating value ${newVal}`;
    }
    }
    }
    <

    View Slide

  20. HTML Templates

    View Slide

  21. • HTML tag:
    • Parsed, but not rendered
    • Instantiated via JavaScript
    HTML Templates

    My Image Card<

    Description<
    <
    const template = document.querySelector('template');
    const templateInstance = document.importNode(template.content, true);
    const img = templateInstance.querySelector('img');
    img.setAttribute('src', 'http:///a-link.com/foo.png');
    document.body.appendChild(templateInstance);

    View Slide

  22. Shadow DOM

    View Slide

  23. • Encapsulates HTML and CSS
    • Isolated DOM: elements within Shadow DOM are not selectable from outside
    • e.g. document.querySelector() won’t return a result
    • Scoped CSS: CSS styles stay within Shadow DOM, no leakage, no bleed-in
    • Composition: declarative, markup-based API, put elements from outside in
    • Simplifies CSS: no conflicts with existing classes and IDs
    • Productivity: app consists of chunks of components instead of one big page
    Shadow DOM
    https://developers.google.com/web/fundamentals/web-components/shadowdom

    View Slide

  24. • Shadow host
    DOM node that the
    shadow DOM is attached to
    • Shadow tree
    DOM tree inside shadow DOM
    • Shadow boundary
    Place where the shadow DOM
    ends and the regular DOM begins (“Light DOM”)
    • Shadow root
    Root node of the shadow tree
    Shadow DOM
    document
    shadow
    host
    Document Tree
    Shadow Tree
    shadow
    root
    Shadow boundary

    View Slide

  25. • attachShadow() attaches a shadow DOM to any HTML element
    • open and closed mode
    Shadow DOM

    <

    <
    <br/>const shadowHost = document.querySelector('div');<br/>const shadowRoot = shadowHost.attachShadow({ mode: 'open' });<br/>shadowRoot.innerHTML = '<h1>Hello World!<</h1>';<br/><
    <
    <

    View Slide

  26. • open allows access to the HTML element’s shadow DOM
    • closed does not allow access to the HTML element’s shadow DOM
    Shadow DOM
    /// .....
    shadowHost.attachShadow({ mode: 'open' });
    document.querySelector('div').shadowRoot.querySelector('h1');
    /// ->- [HTMLHeadingElement]
    /// .....
    shadowHost.attachShadow({ mode: 'closed '});
    document.querySelector('div').shadowRoot.querySelector('h1');
    /// ->- Can not read 'querySelector' of null

    View Slide

  27. • A (named) placeholder
    to fill with your own
    markup
    • Basically mixes together
    two DOM trees:
    the shadow & light tree
    Shadow DOM - Slots

    <br/>:host { border: 1px solid black; display: block; }<br/><

    <
    With Default Content<
    <
    <
    < -

    Unnamed Slot Content<
    <

    Unnamed Slot Content 2<
    Another Slot Content<
    <

    View Slide

  28. • Slots emit an event, whenever its content changes: slotchange
    • Get the slotted elements via assignedNodes()
    • https://drafts.csswg.org/css-scoping/#slotted-pseudo
    Shadow DOM - Slots
    const slot = this.shadowRoot.querySelector('slot');
    slot.addEventListener('slotchange', () =>= {
    const assignedNodes = slot.assignedNodes();
    /// Do something with assignedNodes
    });

    View Slide

  29. • Styling is done via -tags<br/>• All stylings are local and do not overwrite styles from other shadow DOMs<br/>• No other stylings will bleed into the shadow DOM<br/>Shadow DOM - Styling<br/><template><br/><style><br/>h1 {<br/>color: red;<br/>}<br/><
    Hello World!<
    <
    const shadowRoot = document.querySelector('div')
    .attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = 'Hello World!<' +
    'h1 { color: red; }<';

    View Slide

  30. • Inside shadow DOM
    • :host, :host(), :host-context(), ::slotted()
    • Outside shadow DOM
    • CSS Shadow Parts: ::part()
    • In Discussion: ::theme()
    • Deprecated: >>> (“shadow piercing”)
    Shadow DOM - CSS Selectors

    View Slide

  31. • :host: Selects the shadow host element
    • :host(): Selects the shadow host element only, if it has a certain class
    • :host-context(): Selects the shadow host
    element only, if the selector given as the
    function's parameter matches the shadow
    host's ancestor(s) in the place it sits inside
    the DOM hierarchy
    • :::slotted(): Selects a slotted element
    if it matches the selector
    Shadow DOM - CSS Selectors - Inside Shadow DOM
    document
    shadow
    host
    Document Tree
    Shadow Tree
    shadow
    root
    Shadow boundary
    <

    <
    <
    <
    <

    :::slotted(div) {} <
    <
    <

    View Slide

  32. Shadow DOM - CSS Selectors - Inside Shadow DOM

    <br/>:host {<br/>display: block;<br/>background-color: blue;<br/>}<br/>:host(.red) { background-color: red; }<br/>:host-context(main) { background-color: yellow; }<br/>:::slotted(article) { background-color: black; }<br/><

    Before slot<
    Default Slot content<
    After slot<
    <
    <
    <
    <

    <
    <


    Article Content
    <
    <

    View Slide

  33. • CSS Shadow Parts (Working Draft): :::part()
    • Allows to selectively expose elements from the shadow tree to the
    outside page for styling purposes
    • https://www.w3.org/TR/css-shadow-parts-1/
    • CSS Shadow Parts (Unofficial Draft): :::theme()
    • Matches all given parts, no matter how deeply nested they are
    • http://tabatkins.github.io/specs/css-shadow-parts/#part-theme
    Shadow DOM - CSS Selectors - Outside Shadow DOM

    View Slide

  34. • Allows to selectively expose
    elements from the shadow tree to
    the outside page for styling
    purposes
    Shadow DOM - CSS Selectors - Outside Shadow DOM - ::part

    <br/>header {<br/>background-color: crimson;<br/>}<br/><

    Style me<
    <
    <
    <br/>my-element.styled:::part(my-header) {<br/>background-color: black;<br/>color: white;<br/>}<br/><
    <
    <

    View Slide

  35. • If a nested Web Component
    will expose an inner Web
    Component’s part, it has to
    use exportparts=“part1 part2 …”
    instead of part=“name”
    Shadow DOM - CSS Selectors - Outside Shadow DOM - ::part
    <br/>my-element:::part(textspan) { color: red; }<br/><

    exportparts="innerspan textspan”>
    <
    <

    Innerspan<
    Textspan<
    <
    <

    View Slide

  36. Importing Web Components

    View Slide

  37. • ES6 Import
    • Via Code
    • Deprecated: HTML Import
    Importing Web Components

    <
    /// static import
    import './my-element.js';
    /// dynamic import
    import('./my-element.js')
    .then(.....);
    /// or lazy-loading via
    const script = document.createElement('script');
    script.src = './my-element.js';
    document.body.appendChild(script);
    const myElement = document.createElement('my-element');
    document.body.appendChild(myElement);

    View Slide

  38. CustomElementRegistry

    View Slide

  39. • Window global registry for all custom elements
    • API
    • define(): defines a new element
    • get(): returns the constructor of an element (or undefined)
    • whenDefined(): returns a promise which is fulfilled, when the custom
    element is available
    • upgrade(): upgrades an element before it is connected to its shadow
    root
    CustomElementRegistry

    View Slide

  40. Take Care!
    A sneak peek into the future

    View Slide

  41. Versioning
    My library A
    My library B
    3rd party
    library
    1.0
    3rd party
    library
    2.0
    Application


    defines
    defines
    Uncaught DOMException: Failed to execute ‘define’
    on ‘CustomElementRegistry’: the name “my-web-
    component” has already been used with this
    registry.

    View Slide

  42. • If you wish to sync them, then you’ll need to sync them!
    • “Reflecting properties to attributes” or “Reflected DOM Attributes”
    • Helps to keep DOM representation in sync with its JavaScript state
    • Watch out! Properties can hold objects, numbers etc. Attributes only strings

    Attributes vs Properties
    input = document.createElement('input');
    console.log(input.getAttribute('value')); /// null
    input.value = 'one';
    console.log(input.getAttribute('value')); /// null
    input.setAttribute('value', 'two');
    console.log(input.value); /// one

    View Slide

  43. • Easy things may require more code
    • Attributes vs Properties
    • addEventListener etc.
    • Bigger component libraries need a strong foundation
    • Think of when and how to render your component
    • There is no virtual DOM like in React or Angular
    • You may need to think of partial DOM updates for bigger components
    Boilerplate & render loops

    View Slide

  44. • Forms and Web Components don’t play well yet
    • FormData is not populated
    • does not work
    • Form Participation API aka “Form Attached Controls” is discussion
    • https://github.com/w3c/webcomponents/issues/815
    • https://github.com/w3c/webcomponents/issues/814
    • https://github.com/github/image-crop-element/pull/15
    • https://github.com/axa-ch/patterns-library/issues/1258
    Form Participation API

    View Slide

  45. • Already good possibilities to create framework-independent components
    • Decide between view-only and data-controlling Web Components
    • Keep in mind, what is not working yet, but will come in the future
    • Leverage the shadow DOM to encapsulate your Web Components
    • Think of your Web Components API!
    Web Components - Summary

    View Slide

  46. • Blog Series: “The Perks & Flaws of Web Components”
    • https://bit.ly/web-components-series
    • Web Components Cheat Sheet
    • https://www.thinktecture.com/de/cheatsheet/web-components/
    More info about Web Components

    View Slide

  47. And… now?
    • Try it out! Fiddle around with Web Components
    • Use them! Include first small Web Components into your application
    • Be prepared! Keep up to date with the development of Web Components
    • Slides & Repo: https://thinktecture.com/manuel-rauber
    • Contact: [email protected] - @manuelrauber

    View Slide