微前端之Web組件自定義元素示例詳解
我們知道的
第一:我們熟知的HTML標(biāo)簽有 a, p, div, section, ul, li, h2, article, head, body, strong, video, audio 等等
第二:我們知道,a標(biāo)簽是鏈接,p標(biāo)簽是段落,div是塊級,h2是字體,strong 是粗體,video 可以播放視頻,標(biāo)簽可以添加click等事件等等
所以,那么顯然我們知道這些標(biāo)簽的名稱,而且知道他們的默認的css樣式,而且知道他們默認的js事件
由此,我們是否可以自定義標(biāo)簽?zāi)?,由我們自己?guī)定名稱,規(guī)定默認css樣式,規(guī)定標(biāo)簽?zāi)J顯示dom結(jié)構(gòu),規(guī)定默認事件呢?
答案當(dāng)然是肯定的??!下面就介紹主角Web組件
Web組件使用
名稱規(guī)范
- 在定義它們時必須使用至少兩個單詞和一個連字符
- 必須小寫
- 自定義元素不能自閉合
class UserCard extends HTMLElement { constructor() { super(); } } window.customElements.define('user-card', UserCard);
組件傳參數(shù)并可以寫模板包括js和css
組件的樣式應(yīng)該與代碼封裝在一起,只對自定義元素生效,不影響外部的全局樣式,:host偽類,指代自定義元素本身
<html> <head></head> <body> <user-card name="Marty Mcfly"></user-card> <template id="userCardTemplate"> <div class="profile-picture"> <img src="marty.png" alt="Marty Mcfly" /> </div> <div class="name"></div> <style> :host { display: flex; align-items: center; width: 450px; height: 180px; background-color: #d4d4d4; border: 1px solid #d5d5d5; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); border-radius: 3px; overflow: hidden; padding: 10px; box-sizing: border-box; font-family: 'Poppins', sans-serif; } .image { flex: 0 0 auto; width: 160px; height: 160px; vertical-align: middle; border-radius: 5px; } </style> </template> <script> class UserCard extends HTMLElement { constructor() { super(); var templateElem = document.getElementById('userCardTemplate'); var content = templateElem.content.cloneNode(true); // 使用name參數(shù) content.querySelector('.container>.name').innerText = this.getAttribute('name'); this.appendChild(content); } } window.customElements.define('user-card', UserCard); </script> </body> </html>
Shadow Dom 影子節(jié)點
- 沒有開啟Shadow Dom 前template的內(nèi)容會直接append到user-card節(jié)點上
- 開啟Shadow Dom后,template和user-card中間多了一個節(jié)點叫shadowRoot
- attachShadow 的mode參數(shù)有 open 和 closed 兩個不同參數(shù),區(qū)別是open時,外部能訪問內(nèi)部節(jié)點,closed時完全隔離
- attachShadow 是大多數(shù)標(biāo)簽都支持的,比如 div,p,selection,但是a,ul,li等不支持
<script> class UserCard extends HTMLElement { constructor() { super(); var shadow = this.attachShadow( { mode: 'closed' } ); /******這行開啟shadow*****/ var templateElem = document.getElementById('userCardTemplate'); var content = templateElem.content.cloneNode(true); // 使用name參數(shù) content.querySelector('.container>.name').innerText = this.getAttribute('name'); shadow.appendChild(content); /******這行template添加到shadow*****/ } } window.customElements.define('user-card', UserCard); </script>
類中的構(gòu)造函數(shù)和鉤子函數(shù)
class UserCard extends HTMLElement { // attributeChangedCallback 能監(jiān)聽的屬性 static get observedAttributes() {return ['name', 'url']; } constructor() { super(); // 可以創(chuàng)Shadom,通過this.shadowRoot獲取 // this.attachShadow({ mode: 'open' }) } connectedCallback (){ console.log('鉤子,元素append到ducument觸發(fā)') } disconnectedCallback (){ console.log('鉤子,元素從document刪除觸發(fā)') } // 只有observedAttributes 中監(jiān)聽的屬性name,url變化會觸發(fā)下面回調(diào) attributeChangedCallback (attr, oldVal, newVal){ console.log('鉤子,元素屬性改變時觸發(fā)') } } window.customElements.define('user-card', UserCard);
姓名 | 何時調(diào)用 |
---|---|
constructor | 元素的一個實例被創(chuàng)建。對于初始化狀態(tài)、設(shè)置事件監(jiān)聽器或創(chuàng)建shadow Dom。 |
connectedCallback | 每次將元素插入 DOM 時調(diào)用。一般主要工作代碼寫在這里。 |
disconnectedCallback | 每次從 DOM 中刪除元素時調(diào)用。一般寫清理的一些代碼。 |
attributeChangedCallback(attrName, oldVal, newVal) | 觀察屬性在添加、刪除、更新、替換時被調(diào)用。只有屬性中列出的observedAttributes屬性才會收到此回調(diào)。 |
adoptedCallback | 自定義元素已被移動到一個新的document(例如有人稱為document.adoptNode(el))。 |
getter/setter屬性和屬性反射
html中attribute 和 類中property 是各自獨立,想要建立映射需要手動設(shè)置
getter和setter生效
<html> <head></head> <body> <user-card id="usercard" name="Marty"></user-card> <script> class UserCard extends HTMLElement { _name = null; constructor() { super(); } set name(value) { this._name = name; } get name() { return this._name; } } window.customElements.define("user-card", UserCard); </script> </body> </html>
測試
document.getElementById('usercard').name = 'jack' // 會進入setter document.getElementById('usercard').name // 會進入getter,返回jack document.getElementById('usercard').getAttribute('name') // 不會進入getter,返回Marty document.getElementById('usercard').setAttribute('name','bob') // 不會進入setter document.getElementById('usercard').getAttribute('name') // 不會進入getter,返回bob
此時,我們看到 setAttribute 和 gettAttribute 都不會觸發(fā)類中的 getter 和 setter 方法,但是可以看到如果是 .name 這樣的方式可以觸發(fā) ,那么改造方式如下:
方式一 重寫 setAttribute :
<html> <head></head> <body> <user-card id="usercard" name="Marty"></user-card> <user-card id="uc" name="Marty Mcfly"></user-card> <script> const rawSetAttribute = Element.prototype.setAttribute; class UserCard extends HTMLElement { _name = null; constructor() { super(); Element.prototype.setAttribute = function setAttribute(key, value) { // 特定的指定name if (key == "name") { this.name = value; // 這樣就能觸發(fā) setter } rawSetAttribute.call(this, key, value); }; } set name(value) { debugger; this._name = name; } get name() { debugger; return this._name; } } window.customElements.define("user-card", UserCard); </script> </body> </html>
方式二 重寫 observedAttributes和attributeChangedCallback 監(jiān)聽 name :
<html> <head></head> <body> <user-card id="uc" name="Marty Mcfly"></user-card> <script> class UserCard extends HTMLElement { static get observedAttributes() { return ["name"]; } _name = null; constructor() { super(); } attributeChangedCallback(attr, _oldVal, newVal) { if (attr == "name") { this.name = newVal; } } set name(value) { debugger; this._name = name; } get name() { debugger; return this._name; } } window.customElements.define("user-card", UserCard); </script> </body> </html>
由此,可以看到組件在頁面渲染時會進入setter方法,而且 setAttribute,getAttribute 均進入到setter方法
document.getElementById('usercard').setAttribute('name','bob') // 會進入setter document.getElementById('usercard').getAttribute('name') // 會進入getter,返回bob
擴展原生 HTML
假設(shè)您想創(chuàng)建一個更高級的<button>. 與其復(fù)制<button>的行為和功能,更好的選擇是使用自定義元素逐步增強現(xiàn)有元素。
- 擴展現(xiàn)有元素的主要好處是獲得其所有特性(DOM 屬性、方法、可訪問性)。
- Safari瀏覽器兼容性待提高
- 要擴展一個元素,您需要創(chuàng)建一個繼承自正確 DOM 接口的類定義。前面例子都是繼承HTMLElement,現(xiàn)在更多繼承例如,按鈕HTMLButtonElement,圖片HTMLImageElement
class FancyButton extends HTMLButtonElement { constructor() { super(); this.addEventListener('click', e => this.drawRipple(e.offsetX, e.offsetY)); } drawRipple(x, y) { let div = document.createElement('div'); div.classList.add('ripple'); this.appendChild(div); div.style.top = `${y - div.clientHeight/2}px`; div.style.left = `${x - div.clientWidth/2}px`; div.style.backgroundColor = 'currentColor'; div.classList.add('run'); div.addEventListener('transitionend', e => div.remove()); } } customElements.define('fancy-button', FancyButton, {extends: 'button'});
注意:擴展基礎(chǔ)元素時,對的調(diào)用 define() 略有變化。必需的第三個參數(shù)告訴瀏覽器您正在擴展哪個標(biāo)簽
- 調(diào)用方式一:
<button is="fancy-button" disabled>Fancy button!</button>
- 調(diào)用方式二:
let button = document.createElement('button', {is: 'fancy-button'}); button.textContent = 'Fancy button!'; button.disabled = true; document.body.appendChild(button);
- 調(diào)用范式三
let button = new FancyButton(); button.textContent = 'Fancy button!'; button.disabled = true;
以上就是微前端之Web組件自定義元素示例詳解的詳細內(nèi)容,更多關(guān)于微前端Web組件自定義元素的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一款功能強大的markdown編輯器tui.editor使用示例詳解
這篇文章主要為大家介紹了一款功能強大的markdown編輯器tui.editor使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析
這篇文章主要為大家介紹了Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11