Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
When to use web components
Search
Tim van der Lippe
February 16, 2018
Programming
3
340
When to use web components
My presentation at the FrontendDeveloperLove 2018 conference
Tim van der Lippe
February 16, 2018
Tweet
Share
Other Decks in Programming
See All in Programming
XP, Testing and ninja testing ZOZ5
m_seki
3
400
CSC509 Lecture 05
javiergs
PRO
0
300
The Flutter Journey of Building a Live Streaming App — With a Side of Performance Tuning
u503
1
100
CSC509 Lecture 06
javiergs
PRO
0
250
なぜあの開発者はDevRelに伴走し続けるのか / Why Does That Developer Keep Running Alongside DevRel?
nrslib
3
380
私はどうやって技術力を上げたのか
yusukebe
43
18k
Things You Thought You Didn’t Need To Care About That Have a Big Impact On Your Job
hollycummins
0
190
Serena MCPのすすめ
wadakatu
4
920
高度なUI/UXこそHotwireで作ろう Kaigi on Rails 2025
naofumi
4
3.7k
iOSエンジニア向けの英語学習アプリを作る!
yukawashouhei
0
190
CI_CD「健康診断」のススメ。現場でのボトルネック特定から、健康診断を通じた組織的な改善手法
teamlab
PRO
0
190
monorepo の Go テストをはやくした〜い!~最小の依存解決への道のり~ / faster-testing-of-monorepos
convto
2
440
Featured
See All Featured
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
114
20k
The Cost Of JavaScript in 2023
addyosmani
53
9k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Site-Speed That Sticks
csswizardry
11
890
For a Future-Friendly Web
brad_frost
180
9.9k
Automating Front-end Workflow
addyosmani
1371
200k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
667
120k
Designing Experiences People Love
moore
142
24k
The Power of CSS Pseudo Elements
geoffreycrofte
79
6k
How GitHub (no longer) Works
holman
315
140k
Transcript
When to use web components Tim van der Lippe TimvdLippe
Remote Polymer team member Master student Computer Science Delft University
of Technology
3 accepted standards
Schedule Accepted standards • <template> • Shadow DOM • Custom
Elements Examples
<template> element
Blueprint <template> <header> TODOList: </header> <span>Presentation at: <a href="https://www.frontenddeveloperlove.com/" >FrontendDevelopersLove</a>
</span> </template>
Dynamically creating DOM
Dynamically creating DOM node.innerHTML = ` <header> TODOList: </header> <span>Presentation
at: <a href="https://www.frontenddeveloperlove.com/" >FrontendDevelopersLove</a> </span> `
Dynamically creating DOM const header = document.createElement('header'); header.innerText = `TODOList`;
const span = document.createElement('span'); const a = document.createElement('a'); a.href = `https://www.frontenddeveloperlove.com/`; a.innerText = `FrontendDevelopersLove`; span.appendChild(document.createTextNode('Presentation at:')) span.appendChild(a) node.appendChild(header); node.appendChild(span);
Performance problems Parsing (innerHTML)
Performance problems Creation of JS nodes (document.createElement)
Performance problems DOM manipulation (all)
Dynamically creating DOM const template = document.createElement('template'); template.innerHTML = `
<header> TODOList: </header> <span>Presentation at: <a href="https://www.frontenddeveloperlove.com/" >FrontendDevelopersLove</a> </span>`; node.appendChild(template.content.cloneNode(true));
https://jsperf.com/clonenode-vs-createelement-performance/95 Method Ops/sec Relative speed innerHTML 22,828 32% createElement 31,566
45% createElement (abstraction) 25,440 36% template.content.cloneNode 69,874 100%
When to use <template> Creating DOM based on the same
“template” (e.g. component) Efficiently update DOM after the fact (lit-html) Only creating DOM once “template” only consists of dynamic parts
Shadow DOM
Native elements have encapsulation http://techslides.com/demos/sample-videos/small.mp4
Native elements have encapsulation http://techslides.com/demos/sample-videos/small.mp4
Native elements have encapsulation CSS rules do not reach into
shadow DOM Hide implementation details
CSS scoping/encapsulation Naming convention (BEM)
CSS scoping/encapsulation Runtime class generation (styled-components)
CSS scoping/encapsulation None (and pray)
User-exposed Shadow DOM const div = document.createElement('div'); div.attachShadow({mode: 'open'}); div.shadowRoot.innerHTML
= ` <style> span { color: red; } </style> <span>In Shadow DOM</span>` document.body.appendChild(div); https://dom.spec.whatwg.org/#dom-element-attachshadow
User-exposed Shadow DOM const div = document.createElement('div'); div.attachShadow({mode: 'open'}); div.shadowRoot.innerHTML
= ` <style> span { color: red; } </style> <span>In Shadow DOM</span>` document.body.appendChild(div); https://dom.spec.whatwg.org/#dom-element-attachshadow
User-exposed Shadow DOM const div = document.createElement('div'); div.attachShadow({mode: 'open'}); div.shadowRoot.innerHTML
= ` <style> span { color: red; } </style> <span>In Shadow DOM</span>` document.body.appendChild(div); https://dom.spec.whatwg.org/#dom-element-attachshadow
User-exposed Shadow DOM const div = document.createElement('div'); div.attachShadow({mode: 'open'}); div.shadowRoot.innerHTML
= ` <style> span { color: red; } </style> <span>In Shadow DOM</span>` document.body.appendChild(div); https://dom.spec.whatwg.org/#dom-element-attachshadow
Other (implementation) benefits Cheap style invariance checks Simple CSS selectors
When to use Shadow DOM Encapsulate DOM to prevent taint
by global CSS Hide implementation details Overusing Shadow DOM
Custom Elements
Team Parkour Research + prototype team Alex Russell Polymer Summit
2017 https://www.youtube.com/watch?v=y-8Lmg5Gobw
Team Parkour State-of-the-art back then Alex Russell Polymer Summit 2017
https://www.youtube.com/watch?v=y-8Lmg5Gobw
Team Parkour Identify areas that are common Alex Russell Polymer
Summit 2017 https://www.youtube.com/watch?v=y-8Lmg5Gobw
Component model $.widget('ui.menu', { defaultElement: '<ul>', options: { role: 'menu'
}, _create: function() { this.activeMenu = this.element; this._addClass('ui-menu', 'ui-widget ui-widget-content'); }
Component model goog.ui.MenuItem = function(content, opt_model, opt_domHelper, opt_renderer) { goog.ui.Control.call(
this, content, opt_renderer || goog.ui.MenuItemRenderer.getInstance(), opt_domHelper) this.setValue(opt_model); } goog.inherits(goog.ui.MenuItem, goog.ui.Control);
Dojo dojo.declare('dijit.MenuItem' [dijit._Widget, dijit._Templated, dijit._Contained], { // Summary: A line
item in a Menu Widget } )
Angular React Vue ngOnInit constructor created ngAfterContentChecked componentDidMount mounted ngOnDestroy
componentWillUnmount destroyed ngOnChanges componentDidUpdate updated
Angular React Vue ngOnInit constructor created ngAfterContentChecked componentDidMount mounted ngOnDestroy
componentWillUnmount destroyed ngOnChanges componentDidUpdate updated Native constructor connectedCallback disconnectedCallback attributeChangedCallback
https://xkcd.com/927/
Investigated improvements HTML & DOM JS CSS Custom Elements Classes
CSS Variables <template> Promise Web animations Shadow DOM async/await
Browser standards • •
class HelloWorld extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = 'Hello World!'; } connectedCallback() { console.log('connected!'); } disconnectedCallback() { console.log('disconnected'); } }; customElements.define('hello-world', HelloWorld); <hello-world> </hello-world>
class HelloWorld extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = 'Hello World!'; } connectedCallback() { console.log('connected!'); } disconnectedCallback() { console.log('disconnected'); } }; customElements.define('hello-world', HelloWorld); <hello-world> </hello-world>
class HelloWorld extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = 'Hello World!'; } connectedCallback() { console.log('connected!'); } disconnectedCallback() { console.log('disconnected'); } }; customElements.define('hello-world', HelloWorld); <hello-world> </hello-world>
class HelloWorld extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = 'Hello World!'; } connectedCallback() { console.log('connected!'); } disconnectedCallback() { console.log('disconnected'); } }; customElements.define('hello-world', HelloWorld); <hello-world> </hello-world>
When to use Custom Elements Components on the web (without
libraries) Base layer for library authors Interoperability between components Solutions that do not require a component
Combine together
<profile-picture picture="TimvdLippe"> </profile-picture>
const template = document.createElement('template'); template.innerHTML = ` <style>img { border-radius:
50%; }</style> <img src="domain.com/fallback.png" /> `; class ProfilePicture extends HTMLElement{ constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); this._image = this.shadowRoot.querySelector('img'); } static get observedAttributes() { return ['picture']; } attributeChangedCallback(name, oldValue, newValue) { this._image.src = `domain.com/${newValue}.png`; } } customElements.define('profile-picture', ProfilePicture);
const template = document.createElement('template'); template.innerHTML = ` <style>img { border-radius:
50%; }</style> <img src="domain.com/fallback.png" /> `; class ProfilePicture extends HTMLElement{ constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); this._image = this.shadowRoot.querySelector('img'); } static get observedAttributes() { return ['picture']; } attributeChangedCallback(name, oldValue, newValue) { this._image.src = `domain.com/${newValue}.png`; } } customElements.define('profile-picture', ProfilePicture);
const template = document.createElement('template'); template.innerHTML = ` <style>img { border-radius:
50%; }</style> <img src="domain.com/fallback.png" /> `; class ProfilePicture extends HTMLElement{ constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); this._image = this.shadowRoot.querySelector('img'); } static get observedAttributes() { return ['picture']; } attributeChangedCallback(name, oldValue, newValue) { this._image.src = `domain.com/${newValue}.png`; } } customElements.define('profile-picture', ProfilePicture);
const template = document.createElement('template'); template.innerHTML = ` <style>img { border-radius:
50%; }</style> <img src="domain.com/fallback.png" /> `; class ProfilePicture extends HTMLElement{ constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); this._image = this.shadowRoot.querySelector('img'); } static get observedAttributes() { return ['picture']; } attributeChangedCallback(name, oldValue, newValue) { this._image.src = `domain.com/${newValue}.png`; } } customElements.define('profile-picture', ProfilePicture);
const template = document.createElement('template'); template.innerHTML = ` <style>img { border-radius:
50%; }</style> <img src="domain.com/fallback.png" /> `; class ProfilePicture extends HTMLElement{ constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); this._image = this.shadowRoot.querySelector('img'); } static get observedAttributes() { return ['picture']; } attributeChangedCallback(name, oldValue, newValue) { this._image.src = `domain.com/${newValue}.png`; } } customElements.define('profile-picture', ProfilePicture);
Webcomponent libraries
https://timvdlippe.github.io/react-imgpro/ Based on https://github.com/nitin42/react-imgpro
“And React's component based model was perfect for hiding all
the implementation details in a component”
And Web component based model was perfect for hiding all
the implementation details in a component
Straightforward translation componentDidMount connectedCallback JSX <template> defaultProps observedAttributes
Conclusion
We talked about <template> Shadow DOM Custom Elements
Interoperability
When to use web components <template> Shadow DOM Custom Elements
TimvdLippe