南京做網(wǎng)站建設(shè)搭建的公司app推廣方案策劃
目錄
引言
演變過程
概述
使用方式
創(chuàng)建標簽
定義標簽
使用標簽
獲取標簽
異步定義標簽
升級標簽
完整案例
結(jié)語
相關(guān)代碼
參考文章
引言
隨著項目體量的增大,組件化和模塊化的優(yōu)勢也愈發(fā)明顯了,構(gòu)建可重復(fù)使用、獨立、可互操作的組件變得尤為重要,在JS中我們可以通過class和函數(shù)對代碼解耦,使某段代碼可以復(fù)用。在TS中我們也可以通過模塊對代碼進行模塊化開發(fā),在HTML頁面中同樣有一種技術(shù)可以實現(xiàn)獨立的、可復(fù)用的組件,這便是本篇文章講到的Web Components
Web Components主要包括Custom Elements、Shadow DOM、HTML Templates和JavaScript這四部分,在后文及后續(xù)的文章中我會詳細展開說說
演變過程
在熟悉web組件之前,我們可以了解一下早期的開發(fā)人員如何進行業(yè)務(wù)組件復(fù)用的?
聊聊我使用過的三種復(fù)用方式
一是使用JQ的load()進行ajax請求,將結(jié)果渲染到某個div或者組件中,以我原先公司的頁面為例子,可以將圖中畫框的部分分別在多個html頁面中實現(xiàn),然后使用JQ進行請求加載到主頁的標簽中,達到組件化效果,此時如果要傳遞參數(shù)則可以通過url或者共用localstorage等形式共享狀態(tài)
這樣做確實可以將某個頁面或者標簽?zāi)K進行復(fù)用,但是缺點也很明顯,一是頁面加載是異步的,需要通過ajax請求html文件的形式完成,二是傳參的方式僅限于query的方式,復(fù)雜的參數(shù)支持率較為薄弱
第二種是Iframe的方式,這種方式和jq的load類似,同樣擁有獨立上下文,可以在當前環(huán)境使用自己的CSS和JS,并且在加載時獨立于主頁面。當然這么做的缺點也是有的,和load函數(shù)一樣,它的頁面單獨加載和渲染多出了性能開銷,以及異步加載問題
最后一種是使用JS的代碼進行動態(tài)HTML拼接,介于其強大的兼容性,動態(tài)HTML拼接在早期的前端開發(fā)中被廣泛使用,并且能夠在絕大多數(shù)瀏覽器上良好運行,比如:
function createCustomTag(tagName, text, attributes) {var tag = "<" + tagName;for (var attr in attributes) {if (attributes.hasOwnProperty(attr)) {tag += " " + attr + '="' + attributes[attr] + '"';}}tag += ">" + text + "</" + tagName + ">";return tag;
}
這么做的好處是兼容性高 ,靈活性強,原生JS即可支持;但是其缺點是使JS語法以及CSS樣式的隔離變得困難,代碼可讀性和可維護性降低,最終也被摒棄。
那么回到新生代的web components,它能解決什么問題,又有什么注意點?感興趣的話就接著往下看吧
概述
自定義元素(Custom Elements)在許多框架和UI組件中廣泛使用,比如elementUI:?<el-xxx></el-xxx>;vant:<van-xxx />等。開發(fā)者可以通過創(chuàng)建自定義的HTML元素,使其在頁面中表現(xiàn)和使用類似于內(nèi)置的HTML元素。開發(fā)者通過自定義元素創(chuàng)建自定義的HTML元素,使其在頁面中表現(xiàn)和使用類似于內(nèi)置的HTML元素,它是通過 JavaScript 創(chuàng)建一個自定義元素類,并通過繼承HTMLElement類來定義其行為和樣式。
使用方式
創(chuàng)建標簽
首先是創(chuàng)建標簽,通過繼承HTMLElement類的方式來創(chuàng)建自定義標簽的行為和樣式。
在構(gòu)造函數(shù)中可以進行自定義標簽的初始化工作,例如設(shè)置默認屬性、添加事件監(jiān)聽器等
class MyCustomElement extends HTMLElement {constructor() {super();this.textContent = "my-custom-element"// 自定義元素被創(chuàng)建時的初始化邏輯}
}
此外自定義標簽類可以包含以下幾種函數(shù):
- 連接回調(diào)(connectedCallback):自定義元素被插入到DOM樹中時調(diào)用
- 斷開回調(diào)(disconnectedCallback):自定義元素從DOM樹中刪除時調(diào)用
- 移動回調(diào)(adoptedCallback):當自定義元素被移動到新文檔時調(diào)用
- 屬性變化回調(diào)(attributeChangedCallback):自定義元素的屬性被添加、刪除或修改時調(diào)用
- 靜態(tài)屬性(observedAttributes):指定attributeChangedCallback要監(jiān)聽哪些屬性的數(shù)組
使用示例可以參考以下代碼(具體效果及用法會在后文貼出)
class MyCustomElement extends HTMLElement {constructor() {super();// 自定義元素被創(chuàng)建時的初始化邏輯this.textContent = "my-custom-element"}connectedCallback() {// 元素被插入到DOM時調(diào)用console.log("元素被插入到DOM");}disconnectedCallback() {// 元素從DOM中移除時調(diào)用console.log("元素從DOM中移除");}adoptedCallback() {// 元素被移動到新文檔時調(diào)用console.log("元素移動到新文檔");}attributeChangedCallback(attrName, oldValue, newValue) {// 元素的屬性被添加、刪除或修改時調(diào)用console.log(`${attrName}屬性的舊值:${oldValue},新值:${newValue}`);}// 或使用靜態(tài)屬性代替get方法static get observedAttributes() {// 指定要監(jiān)聽的元素的屬性數(shù)組return ['name', 'date'];}
}
定義標簽
在創(chuàng)建標簽后,我們需要通過customElements.define來定義一個自定義標簽
customElements.define("my-custom-element", MyCustomElement)
tips:需要注意的是創(chuàng)建的標簽的中間必須帶 '-'?短橫線,與原生標簽隔開,比如:custom-element,而像:customElement,-customElement,customElement-,這幾種是無法作為自定義名稱使用的。
define函數(shù)可以傳入三個參數(shù),第三個可選參數(shù)是ElementDefinitionOptions,這個參數(shù)在使用自定義元素繼承時才會用到,當我們定義一個自定義元素時,可以選擇讓它繼承自內(nèi)置的 HTML 元素。參考下面的代碼
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>CustomElements</title>
</head><body><button is="my-custom-button"></button><script>customElements.define("my-custom-button", class extends HTMLButtonElement {constructor() {super()this.textContent = "按鈕";}connectedCallback() {this.addEventListener("click", () => console.log("點擊了"))}}, { extends: "button" })// 繼承自原生的按鈕標簽</script>
</body></html>
需要注意的是, 自定義標簽類同樣需要繼承于對應(yīng)的HTML標簽類(比如:HTMLButtonElement),其次在標簽中使用時需要增加is屬性,相當于是對button進行拓展
使用標簽
使用自定義元素的方式有兩種,分別是通過document.createElement('my-custom-element')和直接在頁面中使用<my-custom-element></my-custom-element>標簽,這和原生語法一致,只需要把常用的div,a,span等標簽換成自定義的標簽即可
<body><my-custom-element>my-custom-element</my-custom-element><script>const ele = document.createElement("my-custom-element")ele.textContent = "my-custom-element"document.body.appendChild(ele)</script>
</body>
獲取標簽
通過customElements.get(elemName)可以獲取標簽為elemName的自定義標簽類,如果在定義之前獲取則顯示未定義
console.log(customElements.get(elemName));// undefined
customElements.define(elemName, MyCustomElement)
console.log(customElements.get(elemName).name);// class MyCustomElement extends HTMLElement {...}
異步定義標簽
使用customElements.whenDefined(elemName)函數(shù)可以在標簽定義時觸發(fā)回調(diào)函數(shù)
setTimeout(() => {customElements.define(elemName, MyCustomElement)
}, 1000)
customElements.whenDefined(elemName).then(console.log)// class MyCustomElement
console.log(customElements.get(elemName));// undefined
升級標簽
升級標簽的目的是將自定義標簽(ele)和自定義標簽的構(gòu)造函數(shù)或類(MyCustomElement)進行綁定,使標簽(ele)可以訪問類(MyCustomElement)的屬性及方法。通過customElements.upgrade(ele)函數(shù)可以對自定義標簽進行升級操作
class MyCustomElement extends HTMLElement {bgColor = "red"constructor() {super();}
}
const elemName = "my-custom-element"
const ele = document.createElement(elemName)
customElements.define(elemName, MyCustomElement)
console.log(Reflect.ownKeys(ele), ele.bgColor);// [] undefined
customElements.upgrade(ele);// 升級ele,使其與自定義標簽類綁定,也就是說可以訪問MyCustomElement的屬性及方法
console.log(Reflect.ownKeys(ele), ele.bgColor);// ['bgColor'] red
完整案例
最后我們結(jié)合上面的知識點,實現(xiàn)一個完整的自定義標簽的示例
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>CustomElements</title>
</head><body><iframe src="./temp.html" width="100" height="100"></iframe><script>const elemName = "my-custom-element"const ele = document.createElement(elemName)const iframeEle = document.querySelector("iframe")class MyCustomElement extends HTMLElement {bgColor = "red"constructor() {super();// 自定義元素被創(chuàng)建時的初始化邏輯this.textContent = elemName}connectedCallback() {// 元素被插入到DOM時調(diào)用console.log("元素被插入到DOM");}disconnectedCallback() {// 元素從DOM中移除時調(diào)用console.log("元素從DOM中移除");}adoptedCallback() {// 元素被移動到新文檔時調(diào)用console.log("元素移動到新文檔");}attributeChangedCallback(attrName, oldValue, newValue) {// 元素的屬性被添加、刪除或修改時調(diào)用console.log(`${attrName}屬性的舊值:${oldValue},新值:${newValue}`);}// 或使用靜態(tài)屬性代替get方法static get observedAttributes() {// 指定要監(jiān)聽的元素的屬性數(shù)組return ['name', 'date'];}}customElements.whenDefined(elemName).then(() => {const tempBox = iframeEle.contentDocument // 獲取iframe的domdocument.body.appendChild(ele)// 元素被插入到DOMele.setAttribute("name", elemName)// name屬性的舊值:null,新值:my-custom-elementele.setAttribute("date", "date")// date屬性的舊值:null,新值:dateele.setAttribute("name", "my-custom-element2")// name屬性的舊值:my-custom-element,新值:my-custom-element2ele.remove()// 元素從DOM中移除// 元素移動到新文檔,通過iframe進行舉例tempBox.body.appendChild(tempBox.adoptNode(ele))// 元素被插入到DOM})// 異步檢查自定義標簽定義,標簽定義時觸發(fā)該函數(shù)console.log(customElements.get(elemName));// 獲取自定義標簽,此時未定義console.log(ele instanceof MyCustomElement); // falseiframeEle.onload = () => {setTimeout(() => {// 加個setTimeout明顯一點customElements.define(elemName, MyCustomElement)console.log(Reflect.ownKeys(ele), ele.bgColor);// [] undefinedcustomElements.upgrade(ele);// 升級ele,使其與自定義標簽類綁定,也就是說可以訪問MyCustomElement的屬性及方法console.log(Reflect.ownKeys(ele), ele.bgColor);// ['bgColor'] redconsole.log(ele instanceof MyCustomElement);// trueconsole.log(customElements.get(elemName).name);// MyCustomElement , 獲取自定義標簽,此時已定義}, 1000);}</script>
</body></html>
上述代碼展示了一個簡單的自定義元素的定義和使用過程,包括自定義元素的構(gòu)造函數(shù)、移動、生命周期回調(diào)方法以及屬性變化回調(diào)方法的使用,以及customElements中的幾種方法
結(jié)語
Custom Elements允許我們創(chuàng)建自定義的HTML元素,使其在頁面中表現(xiàn)和使用類似于內(nèi)置的HTML元素。我們可以通過繼承HTMLElement類來定義自定義元素的行為和樣式,并在構(gòu)造函數(shù)中進行初始化工作。通過類的機制,我們可以達到復(fù)用自定義元素的目的。后面的系列文章我會基于其強大的Api與Shadow DOM和HTML Templates實現(xiàn)組件化效果,敬請期待。
文章到這也就結(jié)束了,感謝你的閱讀,如果覺得文章對你有幫助的話,還望三連支持一下作者,感謝!
相關(guān)代碼
WebComponents/CustomElements.html · 阿宇的編程之旅/myCode - Gitee.com
參考文章
Web components
Web Component - Web API 接口參考 | MDN