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

WebComponents: Native Komponenten fürs Web – mal ohne Framework?

WebComponents: Native Komponenten fürs Web – mal ohne Framework?

Die Begriffe UI-Komponenten oder Controls kennen wir Entwickler seit "Ewigkeiten". Durch Kapselung erhalten wir modulare und wiederverwendbare Baukastenelemente, aus denen unsere UI-Anwendungen zusammengesetzt werden. Bisher mussten wir uns im Web eines SPA Frameworks bedienen. Ob Angular, React, Vue.js oder Polymer. Doch keines dieser Frameworks wollte oder konnte so recht mit dem anderen zusammenarbeiten. Dies könnte sich in Zukunft ändern mit WebComponents. Diese beschreiben anhand von Standards wie CustomElements ein Komponentenmodell für das Web und bietet damit erstmal die Möglichkeit, native Komponenten im Browser gänzlich ohne ein Framework oder aber über Framework-Grenzen hinweg einzusetzen. In dieser Session klärt Manuel Rauber wie WebComponents funktionieren, wo die Vorteile und Nachteile liegen – und, vermutlich am spannendsten: er geht der Frage in gewohnt praktischer Manier nach, ob WebComponents mit aktuellen Single-Page Application (SPA) Frameworks wie Angular oder React genutzt und erstellt werden können. Abgerundet wird das Ganze mit einem Ausblick, wie wir künftig Web-Anwendungen mit Web Components entwickeln könnten. Auf zum nächsten Schritt!

GitHub: https://github.com/thinktecture-labs/web-components-demo

Manuel Rauber

November 19, 2019
Tweet

More Decks by Manuel Rauber

Other Decks in Programming

Transcript

  1. Web Components:
    Native Komponenten für’s Web
    Mal ohne Framework
    Manuel Rauber
    @ManuelRauber
    Consultant

    View Slide

  2. Manuel Rauber
    Consultant @ Thinktecture AG
    [email protected]
    @manuelrauber
    https://manuel-rauber.com
    Microsoft MVP
    Who Am I?

    View Slide

  3. • The challenge
    • Web Components
    • What are they?
    • How to use them?
    • How to create them?
    • Take Care & a peek into the future
    • What about frameworks?!
    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
    Components
    Components
    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. • Web Components are not a standard, but a collection of 3 technologies
    • Custom Elements
    • HTML templates
    • Shadow DOM
    • (HTML Imports)
    • Bring a native component model to web instead of having to use frameworks
    Web Components - What are they?

    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. • Problem: Custom Element Registry is a window global object
    • Possible solution currently discussed by W3C
    • Scoped Custom Element Registry
    • https://github.com/w3c/webcomponents/issues/716
    • Current usable solutions
    • Version your HTML tags
    • The host application is the only one loading WCs, even 3rd-party, but this
    could break other Web Components expecting a certain version
    Versioning

    View Slide

  43. • 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

  44. • Shadow DOM encapsulates the DOM only
    • JavaScript is still global scoped
    • Use common techniques for scoping JavaScript
    • May use the global scope to your advantage for app-wide configurations
    • But global polyfills can cause problems (e.g. if you load zone.js twice)
    JavaScript is still global scoped

    View Slide

  45. • 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

  46. • There is no built-in template engine, you may be used to from SPA
    frameworks like Angular, React, Vue etc.
    • JavaScript template strings are preferred for better readability
    • Can get complex and unreadable when conditions are used heavily
    No built-in template engine

    View Slide

  47. No built-in template engine - Lit Element example
    get previewTemplate() {
    return html`

    ${this.preview.image
    ? html``
    : html``
    }

    ${this.preview.title}!
    ${this.preview.description
    ? html`${this.preview.description}!`
    : html``
    }
    !
    !
    `;
    }

    View Slide

  48. • 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

  49. • 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

  50. What about frameworks?!

    View Slide

  51. • Web Components are an uprising good alternative to build frontends
    without frameworks involved
    • But! A lot of stuff is done easier with frameworks, e.g. Form Validation, State
    Handling, Data Management, CSS post processing
    • Why not wrap framework components into Web Components?
    • Angular: Angular Elements, @angular/elements
    • React: Wrap manually or use a community wrapper
    • Vue: Vue Web Components, @vue/web-components
    What about frameworks?!

    View Slide

  52. • Bundle sizes per framework is quite high for small components
    • Possible long loading and parsing times
    • A solution could be:
    • Bundle Web Components without their framework
    • Load the framework in the hosting application
    • Watch out! All Web Components have to use the same framework version
    • Will get better over time with less and less framework involved
    • or frameworks compile to much smaller bundle sizes (e.g. Angular Ivy)
    What about frameworks?! - Bundle Sizes

    View Slide

  53. DEMO

    View Slide

  54. Conclusion

    View Slide

  55. • Web Components help in giving a semantic structure in apps
    • Improves maintainability and reusability
    • Take care for stuff not working yet (e.g. Form Participation API)
    • SPA Frameworks can help in building Web Components
    • … with the odd of having bigger bundles
    • Web Components will make you think more about your APIs!
    Web Components - Summary

    View Slide

  56. 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: https://speakerdeck.com/manuelrauber
    • Repository: https://github.com/thinktecture-labs/web-components-chat
    • Contact: [email protected] - @manuelrauber

    View Slide