sara harkousse - "web components: it's all rainbows and unicorns! is it?"
TRANSCRIPT
1
Web Components
It's all rainbows andunicorns! Is it?
Sara HARKOUSSE UNITED DEV CONFMinsk, 6,7 April 2017
http://vignette2.wikia.nocookie.net/weirdcommunity/images/a/a2/Rainbow-flow-abstract-backgrounds-for-powerpoint.jpg/revision/latest?cb=201306030855322
SARA HARKOUSSE@Sara_harkousse Tech Lead, front-end Web developer at Dassault Systèmes3D design & PLM software Electronics and Computer Science Engineer by training
About me
@UnitedDevConf #webcomponents3
Women in Tech
Duchess [email protected]
Elles bougent (Girls on The Move)@ellesbougentellesbougent.com
@UnitedDevConf #webcomponents @Sara_harkousse4
Web Components
It's all rainbows andunicorns! Is it?
http://vignette2.wikia.nocookie.net/weirdcommunity/images/a/a2/Rainbow-flow-abstract-backgrounds-for-powerpoint.jpg/revision/latest?cb=201306030855325
"The web components revolution""A Tectonic shift for web development""Web components are a game changer"
Web components'public image
@UnitedDevConf #webcomponents @Sara_harkousse6
Outline of this talk
@UnitedDevConf #webcomponents @Sara_harkousse
Motivation
Refresher on web components
Dive deep
Concluding remarks
7
Motivation
@UnitedDevConf #webcomponents @Sara_harkousse
Modular architectureFilterable
Product Table
Search Bar Product Table
ProductRow
ProductCategory
Row
https://facebook.github.io/react/docs/thinking-in-react.html8
@UnitedDevConf #webcomponents @Sara_harkousse
Angular 4.0 Componentimport { Component } from '@angular/core';
@Component({ selector: 'my-app', template: `<h1>Hello {{name}}</h1>`})
export class AppComponent { name = 'Angular'; }
<my-app><h1>Hello Angular</h1></my-app>
https://embed.plnkr.co/?show=preview&show=app%2Fapp.component.ts9
@UnitedDevConf #webcomponents @Sara_harkousse
Ember 2.12.0 Component
<article class="blog-post"> <h1>{{title}}</h1> <p>{{yield}}</p> <p>Edit title: {{input type="text" value=title}}</p></article>
{{#each model as |post|}} {{#blog-post title=post.title}} {{post.body}} {{/blog-post}}{{/each}}
https://guides.emberjs.com/v2.12.0/components/defining-a-component/10
@UnitedDevConf #webcomponents @Sara_harkousse
Polymer 2.0 Element // Define the class for a new element called custom-element class CustomElement extends Polymer.Element { static get is() { return "custom-element"; } constructor() { super(); this.textContent = "I'm a custom-element."; } } // Register the new element with the browser customElements.define(CustomElement.is, CustomElement);
<custom-element></custom-element>
http://plnkr.co/edit/PaCt2M?p=preview11
@UnitedDevConf #webcomponents @Sara_harkousse
Frameworks make ithard to get along
12
@UnitedDevConf #webcomponents @Sara_harkousse
Harmony happenswhen there is less
learning curve
13
@UnitedDevConf #webcomponents @Sara_harkousse
Browser support
https://www.webcomponents.org/
use webcomponents.js
14
Refresher on webcomponents
@UnitedDevConf #webcomponents @Sara_harkousse
HTML Template Tag
HTML Imports
Custom Elements
Shadow DOM
15
HTML Template Tag
@UnitedDevConf #webcomponents @Sara_harkousse
<template id="template"> <div id="container"> <img class="webcomponents" src="https://webcomponents/imgs/webcomponents-logo.svg"> </div> </template>
https://jsfiddle.net/sara_harkousse/rnu8zc0c/16
HTML Template Tag Usage
@UnitedDevConf #webcomponents @Sara_harkousse
var template = document.querySelector('#template');var clone = document.importNode(template.content, true);var host = document.querySelector('#host');host.appendChild(clone);
https://jsfiddle.net/sara_harkousse/rnu8zc0c/17
Template Tag drawbacks?
@UnitedDevConf #webcomponents @Sara_harkousse18
HTML Template Tag
@UnitedDevConf #webcomponents @Sara_harkousse
A way to add inert DOM elements to yourdocument
Not something that's going to revolutionize yourapps
No two-way data binding, no binding at all
19
HTML Imports
@UnitedDevConf #webcomponents @Sara_harkousse
<link rel="import" href="/imports/productRow.html">
<script src="js/productRow.js"></script> <template> <!-- content--> <template>
20
How to get mycomponent?
@UnitedDevConf #webcomponents @Sara_harkousse
var link = document.querySelector('link[rel="import"]');var content = link.import;
// Grab DOM from productRow.html's document.var el = content.querySelector('.productRow');document.body.appendChild(el.cloneNode(true));
21
Why not this way?
@UnitedDevConf #webcomponents @Sara_harkousse
var el = require('/imports/productRow');
22
@UnitedDevConf #webcomponents @Sara_harkousse
<!doctype html><html lang="en">
<head> <link rel="import" href="/imports/productRow.html"> </head>
<body> <!-- index content --> </body>
</html>
Render blocking
23
@UnitedDevConf #webcomponents @Sara_harkousse
<!doctype html><html lang="en">
<head> <link rel="import" href="/imports/productRow.html" async> </head>
<body> <!-- index content --> </body>
</html>
async unblocks the renderer
24
@UnitedDevConf #webcomponents @Sara_harkousse
Hammering ourserver
Each one of the HTML imports is anotherXMLHttpRequest
25
@UnitedDevConf #webcomponents @Sara_harkousse
Build tools for HTMLImport
e.g. Vulcanizr (turns all imports into oneimport)
26
@UnitedDevConf #webcomponents @Sara_harkousse
HTML Importrequires a polyfill
ES6 module?Note: polyfill doesn’t work with file:/// protocols because it does a hidden XMLHttpRequest.
https://www.webcomponents.org/27
@UnitedDevConf #webcomponents @Sara_harkousse
Shadow DOM
A tiny document
exists inside of a host element
28
@UnitedDevConf #webcomponents @Sara_harkousse
Key concepts
Isolated DOM
Scoped CSS
Rendering of shadow tree replaces thatof host tree
29
@UnitedDevConf #webcomponents @Sara_harkousse
Number of shadowtrees
How many shadow trees are too many?
30
@UnitedDevConf #webcomponents @Sara_harkousse
What's inside theShadow DOM?
What to put in the Shadow DOM and what not to?
<my-app>
31
@UnitedDevConf #webcomponents @Sara_harkousse
Shadow DOMrequires a polyfill
Hard to polyfill
32
@UnitedDevConf #webcomponents @Sara_harkousse
Use/Don'tuseShadowDOM?
callback() { // Use it this.root = this.attachShadow({mode: 'open'});
var template = document.querySelector('#template'); var clone = document.importNode(template.content, true); this.root.appendChild(clone);}
callback() { // Don't Use it this.root = this;
var template = document.querySelector('#template'); var clone = document.importNode(template.content, true); this.root.appendChild(clone);}
33
@UnitedDevConf #webcomponents @Sara_harkousse
Use/Don't useShadow DOM?
/* Use it */:host { color: red;}
/* Don't use it */custom-component { color: red;}
34
@UnitedDevConf #webcomponents @Sara_harkousse
Use AND Don't useShadow DOM
if (shadowflag){ this.root = this.attachShadow({mode: 'open'});} else { this.root = this;}
custom-component, :host { color: red;}
35
@UnitedDevConf #webcomponents @Sara_harkousse
Custom Elements
class UnitedDevConButton extends HTMLElement {...}window.customElements.define('unitedDevCon-button', UnitedDevConButton);
<unitedDevCon-button></unitedDevCon-button>
Acts like a div
36
@UnitedDevConf #webcomponents @Sara_harkousse
Custom Elements
class UnitedDevConButton extends HTMLButtonElement {...}window.customElements.define('unitedDevCon-button', UnitedDevConButton, {extends: 'button'});
<button is="unitedDevCon-button" disabled>My button!</button>
Acts like a real button
37
@UnitedDevConf #webcomponents @Sara_harkousse
Custom ElementsNamespace
Dash (-) rule
38
@UnitedDevConf #webcomponents @Sara_harkousse
Custom ElementsES6 Class
class UnitedDevCon-component extends HTMLElement { constructor() { super(); // always call super() first in the ctor. ... } connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attrName, oldVal, newVal) { ... }
}
39
@UnitedDevConf #webcomponents @Sara_harkousse
Component example Bootstrap Progress Bar
<div class="progress"> <div class="progress-bar" style="width: 60%;"> 60% </div></div>
40
@UnitedDevConf #webcomponents @Sara_harkousse
Component exampleclass ProgressBar extends HTMLElement {
constructor() { super(); // always call super() first in the ctor. this.shadow = this.attachShadow({mode: 'open'}); this._complete = 0; }
get complete() { return this._complete; }
set complete(val) { this.setAttribute('complete', val); } }
https://jsfiddle.net/sara_harkousse/kwwe75yy/41
@UnitedDevConf #webcomponents @Sara_harkousse
class ProgressBar extends HTMLElement { static get observedAttributes() { return ['complete']; }
attributeChangedCallback(name, oldval, newval) { // code } }
https://jsfiddle.net/sara_harkousse/kwwe75yy/
Component example
42
@UnitedDevConf #webcomponents @Sara_harkousse
class ProgressBar extends HTMLElement {
attributeChangedCallback(name, oldval, newval) { var innerBar = this.shadow.querySelector('.progress-bar-inner'); switch(name) { case 'complete': this._complete = parseInt(newval, 10) || 0; innerBar.style.width = this.complete + '%'; innerBar.innerHTML = this.complete + '%'; } } }
https://jsfiddle.net/sara_harkousse/kwwe75yy/
Component example
43
@UnitedDevConf #webcomponents @Sara_harkousse
class ProgressBar extends HTMLElement { connectedCallback() { var template = `<style> .progress-bar { width: 50%; ... } .progress-bar-inner { height: 100%; .... } </style> <div class="progress-bar"> <div class="progress-bar-inner">${this.complete}%</div> </div>`; this.shadow.innerHTML = template; }}
https://jsfiddle.net/sara_harkousse/kwwe75yy/
Component example
44
@UnitedDevConf #webcomponents @Sara_harkousse
window.customElements.define('progress-bar', ProgressBar);
// Use<progress-bar></progress-bar>
Component example
45
@UnitedDevConf #webcomponents @Sara_harkousse
Componentson a page
<!doctype html><html lang="en">
<head> <link rel="import" href="/imports/productRow.html"> </head>
<body> <custom-navbar style="compact"></custom-navbar> <custom-toolbar style="full"></custom-toolbar> <custom-pagecontent> <custom-item attr="val"></custom-item> <custom-input attr="val"></custom-input> <progress-bar></progress-bar> </custom-pagecontent>
<script> document.querySelector('progress-bar').addEventListener( 'customevent', function () { // do something } }); </script> </body>
</html>
46
@UnitedDevConf #webcomponents @Sara_harkousse
General concerns
Should you load the polyfills inevery bowser? No.
47
@UnitedDevConf #webcomponents @Sara_harkousse
var = webComponentsSupported = ('customElements' in window && !!HTMLElement.prototype.attachShadow && 'import' in document.createElement('link') && 'content' in document.createElement('template'));
function loadScript(src) { return new Promise(function(resolve, reject) { const script = document.createElement('script'); script.src = src; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); });}
// Lazy load the polyfill if necessary.if (!supportsCustomElementsV1) { loadScript('webcomponents-lite.min.js').then(e => { // Polyfill loaded. });} else { // Native support. Good to go.}
48
@UnitedDevConf #webcomponents @Sara_harkousse
Complexdocumentation
not fun documents to read
49
@UnitedDevConf #webcomponents @Sara_harkousse
Takeaway
You will understand some of the design decisions ofmajor frameworks if you learn about customelements
50
@UnitedDevConf #webcomponents @Sara_harkousse
Takeaway
Who is using web components?
51
@UnitedDevConf #webcomponents @Sara_harkousse
Takeaway
GitHub Mozilla VR Google
52
@UnitedDevConf #webcomponents
Thanks for listening!
Follow for slides@Sara_harkousse
53