国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運(yùn)營 > Web Componets 實(shí)踐報告,原生組件開發(fā)

Web Componets 實(shí)踐報告,原生組件開發(fā)

時間:2023-05-29 00:15:02 | 來源:網(wǎng)站運(yùn)營

時間:2023-05-29 00:15:02 來源:網(wǎng)站運(yùn)營

Web Componets 實(shí)踐報告,原生組件開發(fā):

前言

相信作為一名前端開發(fā),無論是使用vue還是react、augular,大家對于組件和組件化開發(fā)一定不會陌生,甚至頗有道行,但說起原生組件開發(fā),可能許多朋友就撓頭了,還有這個操作么?就好像經(jīng)常用谷歌開發(fā)的人聽說IE突然支持ECMA規(guī)范了,但Web Componets可不是什么新技術(shù),接下來就和我一起了解一下這個‘新’技術(shù)吧。

Web Components 介紹

Web Components 是一套不同的技術(shù),允許您創(chuàng)建可重用的定制元素(它們的功能封裝在您的代碼之外)并且在您的web應(yīng)用中使用它們。具體的介紹可以參照文檔。Web Components | MDN (mozilla.org), 這里要說的是web components的三大api,以及踩到的坑。

Custom elements(自定義元素)

讓我們來嘗試第一次自己定義元素。

<!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>Document</title></head><body> <hello-custom></hello-custom> <hello-custom></hello-custom> <hello-custom></hello-custom></body><script> class HelloCustom extends HTMLElement { constructor() { super(); let hello = document.createElement('div'); hello.innerHTML = "測試一下自定義元素" this.append(hello) } } window.customElements.define('hello-custom', HelloCustom);</script></html>





我們可以發(fā)現(xiàn),主要就是使用一個類來繼承原有的html標(biāo)簽的類型。然后在構(gòu)造函數(shù)中創(chuàng)建需要的元素,最后記得使用this.append方法把元素加入當(dāng)前自定義標(biāo)簽。一個自定義標(biāo)簽類就實(shí)現(xiàn)了,然后在全局對象customElements調(diào)用define方法注冊我們的組件注意:自定義標(biāo)簽必須是中間有一個連字符,瀏覽器默認(rèn)單個單詞是原生標(biāo)簽,有連字符的為自定義標(biāo)簽
能夠看到,用js生成dom確實(shí)是一件很麻煩的事情,所以我們可以使用第二個API:HTML templates(HTML模板)

HTML templates(HTML模板)

<body> <template id="hello"> <p>測試一下hello</p> <a href="">這里是測試的模板</a> </template></body>


可以看到,頁面一片空白,顧名思義,template標(biāo)簽只是一個模板,供給組件使用,本身不會被渲染,看到這個是不是想到了什么,是不是跟vue的一模一樣,其實(shí)vue的設(shè)計(jì)就是受到了原生組件的影響,只是vue設(shè)計(jì)的更加深入,功能更多就是了,現(xiàn)在vue3已經(jīng)支持使用原生組件了,感興趣的可以去看看。Vue 與 Web Components | Vue.js (vuejs.org) 言歸正傳,我們怎么使用template,其實(shí)也很簡單,其中因?yàn)槟0迨强赡鼙粡?fù)用的,所以我們不能直接使用模板源,需要復(fù)制一份
var content = hello.content.cloneNode(true);。

class HelloCustom extends HTMLElement { constructor() { super(); let hello = document.getElementById("hello") var content = hello.content.cloneNode(true); this.append(content) } }


看到這里有聰明的讀者就要問了,那樣式怎么寫呢,難道也用js寫上去么,其實(shí)在template標(biāo)簽里面可以寫一個style標(biāo)簽的。例如:

<template id="hello"> <style> p { background-color: blue; color: white; } </style> <p>測試一下hello</p> <a href="">這里是測試的模板</a> </template>


到這里看起來原生組件已經(jīng)構(gòu)建好了,樣式組件都寫好了,但我在開發(fā)的過程中又發(fā)現(xiàn)了一個問題,那就是template渲染出來的元素是真實(shí)存在的,它的樣式可以被外部css改變,例如我這樣寫:

<style> #htllo_test { background-color: red; } </style> <template id="hello"> <style> p { background-color: blue; color: white; } </style> <p id="htllo_test">測試一下hello</p> <a href="">這里是測試的模板</a> </template> ```![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f1907d1569174bfa8c68b38b839688d5~tplv-k3u1fbpfcp-watermark.image?) 外部的css樣式作用到了組件上面,并且權(quán)重更高,把組件內(nèi)部樣式給覆蓋了,原本的藍(lán)色背景,變成了紅色。用過vue的同學(xué)都知道,vue的style標(biāo)簽上面有一個scope屬性,可以防止外部樣式作用到內(nèi)部組件上面,在我們的原生組件上面,我們要怎么實(shí)現(xiàn)呢,這里就用到了三大api中的最后一個,**Shadow DOM(影子DOM)**### Shadow DOM(影子DOM) &nbsp;&nbsp;所謂的影子dom,見名知意,就是可以屏蔽外部的影響,很簡單吧。我們就不搞那些花里胡哨的說法了,直接開整。 ```js class HelloCustom extends HTMLElement { constructor() { super(); this.shadow = this.attachShadow({mode:'closed'}) // open和closed分別控制是否能被外部js訪問 let hello = document.getElementById("hello") var content = hello.content.cloneNode(true); this.shadow.append(content) } } ``` 可以發(fā)現(xiàn),使用非常的簡單,只需要加入一條代碼 `this.shadow = this.attachShadow({mode:'open'})` 然后在選擇添加的時候把我們的內(nèi)容添加到this.shadow上面就可以了。![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aeb879d690ec49de86e26143b7227a92~tplv-k3u1fbpfcp-watermark.image?) &nbsp;&nbsp;再次查看效果,可以發(fā)現(xiàn),外部樣式已經(jīng)無法影響到內(nèi)部,并且,內(nèi)部的細(xì)節(jié)以及被隱藏,無法查看內(nèi)部結(jié)構(gòu)了,這是因?yàn)槲覀冊谑褂胹hadow dom的時候選擇的*mode:closed*,如果選擇open,則為可以查看。它的本質(zhì)是,是否允許外部js直接修改操作組件內(nèi)部dom。<br>&nbsp;&nbsp;使用了Shadow DOM之后,可以在template中使用Shadow DOM的專用選擇器*host*選擇器,它選擇的是整個template本身。值得注意的是,它必須顯示指定display屬性,否則不可見。![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2b9aad0e51d34d7bbbc73338e8d609cd~tplv-k3u1fbpfcp-watermark.image?)&nbsp;比如鼠標(biāo)劃上自定義元素,左側(cè)無法框選,也無法選中。設(shè)置display之后:```html <template id="hello"> <style> :host { display: block; border: 5px solid gray; } p { background-color: blue; color: white; } </style> <p id="htllo_test">測試一下hello</p> <a href="">這里是測試的模板</a> </template>





自定義組件生命周期

像各大框架的組件一樣,元素組件也是有自己的生命周期的,這一點(diǎn)特別重要。至于生命周期是什么,我想不用我再過贅述,直接上代碼。

class HelloCustom extends HTMLElement { // 生命周期 - created constructor() { super(); console.log('created-被執(zhí)行'); this.shadow = this.attachShadow({ mode: 'closed' }) // open和closed分別控制是否能被外部js訪問 let hello = document.getElementById("hello") var content = hello.content.cloneNode(true); this.shadow.append(content) } // 生命周期 - mounted connectedCallback() { console.log('mounted-被執(zhí)行'); } // 生命周期 - update attributeChangedCallback() { console.log('update-被執(zhí)行'); } // 生命周期 - destory disconnectedCallback() { console.log('被移出') } }


這里我把這些生命周期和vue生命周期對應(yīng)起來,方便大家理解和記憶。 - constructor 是構(gòu)造函數(shù),相當(dāng)于created生命周期 - connectedCallback 相當(dāng)于mounted生命周期 - attributeChangedCallback 相當(dāng)于update生命周期,其中這里的update指的是,組件上面掛載的參數(shù)被更改 - disconnectedCallback 相當(dāng)于destory生命周期

使用slot插槽

你沒有看錯,在原生組件里面可以使用插槽,使用方式和vue基本上一致,可能后者借鑒了一下前者。使用方法如下:

<template id="hello"> <style> :host { display: block; border: 5px solid gray; } p { background-color: blue; color: white; } </style> <p id="htllo_test"></p> <slot>slot default</slot> <slot name="slot1">slot1</slot> <a href="">這里是測試的模板</a> <slot name="slot2">slot2</slot> </template> <hello-custom title="外部傳參測試" id="hello_custom"> <div>我是默認(rèn)slot</div> <div slot="slot1">name slot1</div> </hello-custom>


可以看到,沒有name屬性的slot自動變成默認(rèn)插槽,組件里面同樣沒有slot屬性的元素就會被默認(rèn)放到其中,然后slot和name屬性一一對應(yīng)的就放過去。沒有的就顯示默認(rèn)的內(nèi)容,非常的簡單,和vue基本一致。

使用經(jīng)驗(yàn)總結(jié)及踩坑

可以外部傳參和修改

組件必然是可以內(nèi)外交流的,外部傳參是不可避免的一項(xiàng),使用方式十分簡單。只需要在原生組件上面掛載參數(shù),然后內(nèi)部獲取即可。例如:

<hello-custom title="外部傳參測試"></hello-custom>class HelloCustom extends HTMLElement { // 生命周期 - created constructor() { super(); this.shadow = this.attachShadow({ mode: 'closed' }) // open和closed分別控制是否能被外部js訪問 let hello = document.getElementById("hello") var content = hello.content.cloneNode(true); content.querySelector("#htllo_test").innerHTML = this.getAttribute("title") this.shadow.append(content) } }


使用this.getAttribute就可以獲取掛載在外部的參數(shù),然后想怎么操作就怎么操作

其中原生組件也和其他標(biāo)簽一樣,可以被選擇和操作。可以掛載id和class被css操作,這里就不贅述css的操作了,大家下去可以自行嘗試。(上面說的影子dom可以屏蔽外部修改,指的是屏蔽對內(nèi)部dom的修改,組件整體還是可以改的,比如大小,背景什么的,聯(lián)合vue的組件使用方式思考)。

js操作元素數(shù)據(jù)需要使用生命周期attributeChangedCallback,當(dāng)外部參數(shù)被改變時,該生命周期會執(zhí)行。操作如下:

<hello-custom title="外部傳參測試" id="hello_custom"></hello-custom> <button class="test_btn" onclick="test()">測試改變</button>class HelloCustom extends HTMLElement { // 生命周期 - created constructor() { super(); this.shadow = this.attachShadow({ mode: 'closed' }) // open和closed分別控制是否能被外部js訪問 let hello = document.getElementById("hello") this.content = hello.content.cloneNode(true); this.shadow.append(this.content) } attributeChangedCallback(name, oldValue, newValue) { this.shadow.querySelector("#htllo_test").innerHTML = newValue console.log("被執(zhí)行", newValue); } static get observedAttributes() {return ['title', 'l']; } } window.customElements.define('hello-custom', HelloCustom); function test() { let myHello = document.getElementById('hello_custom') myHello.setAttribute("title", "修改后的外部參數(shù)測試" + new Date()) // console.log(myHello); }需要注意的是,如果需要在元素屬性變化后,觸發(fā) attributeChangedCallback()回調(diào)函數(shù),你必須監(jiān)聽這個屬性。這可以通過定義observedAttributes() get函數(shù)來實(shí)現(xiàn),observedAttributes()函數(shù)體內(nèi)包含一個 return語句,返回一個數(shù)組,包含了需要監(jiān)聽的屬性名稱。static get observedAttributes() {return ['title', 'l']; } 可以看到,已經(jīng)成功得到回調(diào)。







外部修改內(nèi)部子元素

如果mode設(shè)置為open,那么可以在外部獲取之后直接修改組件子元素,通常我們不建議這么做,更好的做法是使用上面的方式在組件內(nèi)部進(jìn)行監(jiān)聽,外部修改組件屬性即可,這里作為一個知識點(diǎn)講解,了解即可。

function test() { let myHello = document.getElementById('hello_custom') myHello.setAttribute("title", "修改后的外部參數(shù)測試" + new Date()) // console.log(myHello); console.log(myHello.shadowRoot); }


在內(nèi)部獲取的時候需要注意,document是無法獲取模板里面的元素的,在constructor生命周期之后,要獲取dom就要使用this.shoadow

后續(xù)的思考和原生組件的缺陷的討論

原生組件的缺陷:

  1. 原生組件的兼容性一般,至少IE不支持,無法通過babel彌補(bǔ),需要兼容IE的請慎重。
  2. 原生組件本質(zhì)上也是js渲染,和vue沒有太大的區(qū)別,所以,如果頁面大量使用,SEO優(yōu)化會比較麻煩,簡而言之,就是還不如使用vue呢。
  3. 無法支持模塊導(dǎo)出template標(biāo)簽,template只能寫到html里面
  4. shadow DOM 隔離了外部js和css,這導(dǎo)致全局的css工具無法使用,如果不使用shadow DOM,則組件有被外部樣式污染的可能。
  5. 原生開發(fā)很麻煩,要使用基本上要結(jié)合webpack

后續(xù)的思考

以上幾點(diǎn)問題是實(shí)際使用中所遇到的問題
其中第三點(diǎn),可以在webpack中,使用ejs模板解決,因?yàn)閑js模板導(dǎo)出的是一個可以返回當(dāng)前模板字符串的一個函數(shù),只需要使用js轉(zhuǎn)一下即可做到像導(dǎo)出js一樣導(dǎo)出template.
第四點(diǎn)。Shadow DOM對全局CSS工具的隔離性暫時無法解決,我選擇的方法是妥協(xié),不使用Shadow DOM,如果有讀者有更好的方法,歡迎評論區(qū)交流。
我覺得最麻煩的還是這個東西本身無法像vue一樣實(shí)現(xiàn)雙向綁定,沒有MVVM的數(shù)據(jù)視圖綁定,可能需要大佬自行實(shí)現(xiàn),那問題就來了,都到這一步了,我為什么不用vue呢。。。。

支持程度

注意: Firefox、Chrome和Opera默認(rèn)就支持 custom elements。Safari目前只支持 autonomous custom elements(自主自定義標(biāo)簽),而 Edge也正在積極實(shí)現(xiàn)中。

關(guān)鍵詞:實(shí)踐,報告

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉