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
320
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
Vapor Revolution
kazupon
1
160
タクシーアプリ『GO』のリアルタイムデータ分析基盤における機械学習サービスの活用
mot_techtalk
5
1.5k
リアーキテクチャxDDD 1年間の取り組みと進化
hsawaji
1
220
React への依存を最小にするフロントエンド設計
takonda
12
3k
受け取る人から提供する人になるということ
little_rubyist
0
250
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
1
100
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
130
ペアーズにおけるAmazon Bedrockを⽤いた障害対応⽀援 ⽣成AIツールの導⼊事例 @ 20241115配信AWSウェビナー登壇
fukubaka0825
6
2k
最新TCAキャッチアップ
0si43
0
200
Micro Frontends Unmasked Opportunities, Challenges, Alternatives
manfredsteyer
PRO
0
110
Kaigi on Rails 2024 〜運営の裏側〜
krpk1900
1
250
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
kno3a87
1
210
Featured
See All Featured
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.5k
Building Applications with DynamoDB
mza
90
6.1k
How to Think Like a Performance Engineer
csswizardry
20
1.1k
Docker and Python
trallard
40
3.1k
It's Worth the Effort
3n
183
27k
Making Projects Easy
brettharned
115
5.9k
Visualization
eitanlees
145
15k
Building Better People: How to give real-time feedback that sticks.
wjessup
364
19k
How To Stay Up To Date on Web Technology
chriscoyier
788
250k
Practical Orchestrator
shlominoach
186
10k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5k
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