【Web技術】1157- Web Components 從 0 到 1

引用 MDN 的話:Web Components?是一套不同的技術,允許您創(chuàng)建可重用的定制元素(它們的功能封裝在您的代碼之外)并且在您的?web?應用中使用它們。
簡單來說就是官方定義的自定義組件的方式,封裝代碼,提高代碼的復用性。相比于第三方框架,原生組件簡單,不需要加載任何外部模塊。
三項主要技術
Custom elements(自定義元素):允許定義? custom elements?及其行為,然后可以在您的用戶界面中按照需要使用它們。Shadow DOM(影子DOM):用于將封裝的"影子" DOM 樹附加到元素(與主文檔 DOM 分開呈現)并控制其關聯的功能。通過這種方式,您可以保持元素的功能私有,這樣它們就可以被腳本化和樣式化,而不用擔心與文檔的其他部分發(fā)生沖突。 HTML templates(HTML模板): ?和??元素使您可以編寫不在呈現頁面中顯示的標記模板。然后它們可以作為自定義元素結構的基礎被多次重用。
生命周期
constructor(創(chuàng)建時):當自定義組建創(chuàng)建時。connectedCallback(掛載時):當自定義元素第一次被連接到文檔 DOM 時被調用。disconnectedCallback(卸載時):當自定義元素與文檔DOM斷開連接時被調用。adoptedCallback(移動時):當自定義元素被移動到新文檔時被調用。attributeChangedCallback(屬性變化時):當自定義元素的一個屬性被增加、移除或更改時被調用。
一個栗子 ??(卡片組件)
按照組件化,通過一個標簽實現這樣一個卡片組件,內容和事件可配置化。

首先實現一個模版:
<template?id="zz_card">
??<style>
????.zz_card_box?{
??????display:?inline-block;
??????min-width:?300px;
??????max-width:?500px;
??????margin:?20px;
??????border:?1px?solid?#ccc;
??????border-radius:?4px;
????}
????.zz_card_head?{
??????padding:?20px;
??????border-bottom:?1px?solid?#ccc;
????}
????.zz_card_body?{
??????padding:?20px;
??????color:?red;
????}
??style>
??<div?class="zz_card_box">
????<div?class="zz_card_head">
??????卡片頭部
????div>
????<div?class="zz_card_body">
??????卡片內容
????div>
??div>
template>
使用?customElements?創(chuàng)建自定義組件:
//?自定義元素需要使用?JavaScript?定義一個類
class?ZzCard?extends?HTMLElement?{
??constructor()?{
????super()
????let?templateElem?=?document.getElementById('zz_card')
????let?content?=?templateElem.content.cloneNode(true)
????this.appendChild(content)
??}
}
//?定義和注冊一個新的自定義元素
window.customElements.define('zz-card',?ZzCard)
現在就可以直接使用標簽來展示卡片
??<zz-card>zz-card>
接下來開始完善組件功能,添加自定義?title,通過slot(插槽)添加內容
<zz-card?title="卡片頭部">
??
??<div?slot="body">卡片內容div>?
??
??<div>卡片內容div>?
zz-card>
<template?id="zz_card">
??<style>
????.zz_card_box?{
??????display:?inline-block;
??????min-width:?300px;
??????max-width:?500px;
??????margin:?20px;
??????border:?1px?solid?#ccc;
??????border-radius:?4px;
????}
????.zz_card_head?{
??????padding:?20px;
??????border-bottom:?1px?solid?#ccc;
????}
????.zz_card_body?{
??????padding:?20px;
??????color:?red;
????}
??style>
??<div?class="zz_card_box">
????<div?class="zz_card_head">div>
????<div?class="zz_card_body">
??????
??????<slot?name="body">slot>
??????
??????<slot>slot>
????div>
??div>
template>
class?ZzCard?extends?HTMLElement?{
??constructor()?{
????super()
????let?templateElem?=?document.getElementById('zz_card')
????let?content?=?templateElem.content.cloneNode(true)
????//?獲取?DOM?設置傳入的屬性
????content.querySelector('.zz_card_head').innerHTML?=?this.getAttribute('title')
????this.appendChild(content)
??}
}
window.customElements.define('zz-card',?ZzCard)
shadow DOM(影子 DOM)改造,我們需要組件內部獨立(樣式,事件等),借用 shadow 可以實現:Element.attachShadow(shadowRootInit)
class?ZzCard?extends?HTMLElement?{
??constructor()?{
????super()
????let?shadow?=?this.attachShadow({?mode:?'open'?})
????let?templateElem?=?document.getElementById('zz_card')
????let?content?=?templateElem.content.cloneNode(true)
????//?獲取?DOM?設置傳入的屬性
????content.querySelector('.zz_card_head').innerHTML?=?this.getAttribute('title')
????shadow.appendChild(content)
??}
}
window.customElements.define('zz-card',?ZzCard)
內部的樣式不會影響外部

生命周期的使用
class?ZzCard?extends?HTMLElement?{
??constructor()?{
????super()
????this._shadow?=?this.attachShadow({?mode:?'open'?})
????const?templateElem?=?document.getElementById('zz_card')
????const?content?=?templateElem.content.cloneNode(true)
????this._shadow.appendChild(content)
??}
??// connectedCallback(掛載時)用于:獲取數據,設置數據
??connectedCallback()?{
????content.querySelector('.zz_card_head').innerHTML?=?this.getAttribute('title')
??}
??// attributeChangedCallback(屬性變化時)用于:數據變更更新組件
??//?方法的觸發(fā)依賴于?get?函數?observedAttributes?實現
??static?get?observedAttributes()?{
????return?['title']
??}
??attributeChangedCallback(name,?oldValue,?newValue)?{
????//?當組件的屬性?title?變化時觸發(fā)
????this._shadow.querySelector('.zz_card_head').innerHTML?=?newValue
??}
??// disconnectedCallback(卸載時)用于:卸載事件
??disconnectedCallback()?{}
}
window.customElements.define('zz-card',?ZzCard)
應用場景
國外一些公司,像?Github、 Youtube?這樣的公司甚至已經在生產環(huán)境中使用?Web Components。
多端組件庫:由于使用的是普通?
js?來編寫,所以可以兼容市面上常用的框架?Vue,React?等,可以說只要支持?html,css?和?js?就可以使用。市面上現有的組件庫:xy-ui,Omi,hybrids,lit,slim.js
微前端(一種多個團隊通過獨立發(fā)布功能的方式來共同構建現代化 web 應用的技術手段及方法策略):根據下圖我們可以看出這種方式其實和?
iframe?的方式很相似,組件擁有獨立的?Scripts?和?Styles,Web Components?是有能力以組件加載的方式將微應用整合在一起作為微前端的一種手段。
優(yōu)缺點
優(yōu)點:
它們是基于 web 標準,不需要安裝任何框架或庫就可以使?,可以通過普通的? js?編寫 web 組件。包體積,相較于其它第三方的框架組件相比,會很小。 性能方面使用原生,無需額外的插件,編譯,會很快。
缺點:
有兼容性 組件樣式修改,在使用 antd,Element等組件庫時,可以直接使用類名的方式修改,但? Web Components?的?css?防污染機制讓我們很難修改內部樣式。
小結
Web Components?相比第三方框架,原生組件簡單直接,符合直覺,不用加載任何外部模塊,代碼量小,不需要一堆工程化工具。還有就是用過?Vue?的同學可能會發(fā)現,Web Components?標準和?Vue?非常像。目前市面上有一些基于?Web Component?標準開發(fā)的組件庫:Omi,xy-ui。

回復“加群”與大佬們一起交流學習~
點擊“閱讀原文”查看 130+ 篇原創(chuàng)文章
