亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁(yè) > 編程 > HTML > 正文

你知道原生HTML組件是什么嗎?原生HTML組件的介紹

2020-03-22 19:40:12
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
本篇文章給大家?guī)?lái)的內(nèi)容是關(guān)于你知道原生HTML組件是什么嗎?原生HTML組件的介紹,有一定的參考價(jià)值,有需要的朋友可以參考一下,希望對(duì)你有所幫助。

嘿!看看這幾年啊,Web 前端的發(fā)展可是真快啊!

想想幾年前,HTML 是前端開發(fā)者的基本技能,通過(guò)各式各樣的標(biāo)簽就可以搭建一個(gè)可用的網(wǎng)站,基本交互也不是問(wèn)題。如果再來(lái)點(diǎn) CSS,嗯,金黃酥脆,美味可口。這時(shí)候再撒上幾把 JavaScript,簡(jiǎn)直讓人欲罷不能。

隨著需求的增長(zhǎng),HTML 的結(jié)構(gòu)越來(lái)越復(fù)雜,大量重復(fù)的代碼使得頁(yè)面改動(dòng)起來(lái)異常困難,這也就孵化了一批批模版工具,將公共的部分抽取出來(lái)變?yōu)楣步M件。再后來(lái),隨著 JavaScript 的性能提升,JavaScript 的地位越來(lái)越高,不再只是配菜了,前端渲染的出現(xiàn)降低了服務(wù)端解析模版的壓力,服務(wù)端只要提供靜態(tài)文件和 API 接口就行了嘛。再然后,前端渲染工具又被搬回了服務(wù)端,后端渲染出現(xiàn)了(黑人問(wèn)號(hào)???)

總之,組件化使得復(fù)雜的前端結(jié)構(gòu)變得清晰,各個(gè)部分獨(dú)立起來(lái),高內(nèi)聚低耦合,使得維護(hù)成本大大降低。

那么,你有聽說(shuō)過(guò)原生 HTML 組件嗎?

1754597496-5bc7f1ee5c154_articlex.jpg

四大 Web 組件標(biāo)準(zhǔn)

在說(shuō)原生 HTML 組件之前,要先簡(jiǎn)單介紹一下四大 Web 組件標(biāo)準(zhǔn),四大 Web 組件標(biāo)準(zhǔn)分別為:HTML Template、Shadow DOM、Custom Elements 和 HTML Imports。實(shí)際上其中一個(gè)已經(jīng)被廢棄了,所以變成“三大”了。

HTML Template 相信很多人都有所耳聞,簡(jiǎn)單的講也就是 HTML5 中的 template 標(biāo)簽,正常情況下它無(wú)色無(wú)味,感知不到它的存在,甚至它下面的 img 都不會(huì)被下載,script 都不會(huì)被執(zhí)行。 template 就如它的名字一樣,它只是一個(gè)模版,只有到你用到它時(shí),它才會(huì)變得有意義。

Shadow DOM 則是原生組件封裝的基本工具,它可以實(shí)現(xiàn)組件與組件之間的獨(dú)立性。

Custom Elements 是用來(lái)包裝原生組件的容器,通過(guò)它,你就只需要寫一個(gè)標(biāo)簽,就能得到一個(gè)完整的組件。

HTML Imports 則是 HTML 中類似于 ES6 Module 的一個(gè)東西,你可以直接 import 另一個(gè) html 文件,然后使用其中的 DOM 節(jié)點(diǎn)。但是,由于 HTML Imports 和 ES6 Module 實(shí)在是太像了,并且除了 Chrome 以外沒(méi)有瀏覽器愿意實(shí)現(xiàn)它,所以它已經(jīng)被廢棄并不推薦使用了。未來(lái)會(huì)使用 ES6 Module 來(lái)取代它,但是現(xiàn)在貌似還沒(méi)有取代的方案,在新版的 Chrome 中這個(gè)功能已經(jīng)被刪除了,并且在使用的時(shí)候會(huì)在 Console 中給出警告。警告中說(shuō)使用 ES Modules 來(lái)取代,但是我測(cè)試在 Chrome 71 中 ES Module 會(huì)強(qiáng)制檢測(cè)文件的 MIME 類型必須為 JavaScript 類型,應(yīng)該是暫時(shí)還沒(méi)有實(shí)現(xiàn)支持。

2508713701-5bc7f09060787_articlex.png

Shadow DOM

要說(shuō)原生 HTML 組件,就要先聊聊 Shadow DOM 到底是個(gè)什么東西。

大家對(duì) DOM 都很熟悉了,在 HTML 中作為一個(gè)最基礎(chǔ)的骨架而存在,它是一個(gè)樹結(jié)構(gòu),樹上的每一個(gè)節(jié)點(diǎn)都是 HTML 中的一部分。DOM 作為一棵樹,它擁有著上下級(jí)的層級(jí)關(guān)系,我們通常使用“父節(jié)點(diǎn)”、“子節(jié)點(diǎn)”、“兄弟節(jié)點(diǎn)”等來(lái)進(jìn)行描述(當(dāng)然有人覺(jué)得這些稱謂強(qiáng)調(diào)性別,所以也創(chuàng)造了一些性別無(wú)關(guān)的稱謂)。子節(jié)點(diǎn)在一定程度上會(huì)繼承父節(jié)點(diǎn)的一些東西,也會(huì)因兄弟節(jié)點(diǎn)而產(chǎn)生一定的影響,比較明顯的是在應(yīng)用 CSS Style 的時(shí)候,子節(jié)點(diǎn)會(huì)從父節(jié)點(diǎn)那里繼承一些樣式。

而 Shadow DOM,也是 DOM 的一種,所以它也是一顆樹,只不過(guò)它是長(zhǎng)在 DOM 樹上的一棵特殊的紫薯,啊不,子樹。

什么?DOM 本身不就是由一棵一棵的子樹組成的嗎?這個(gè) Shadow DOM 有什么特別的嗎?

Shadow DOM 的特別之處就在于它致力于創(chuàng)建一個(gè)相對(duì)獨(dú)立的一個(gè)空間,雖然也是長(zhǎng)在 DOM 樹上的,但是它的環(huán)境卻是與外界隔離的,當(dāng)然這個(gè)隔離是相對(duì)的,在這個(gè)隔離空間中,你可以選擇性地從 DOM 樹上的父節(jié)點(diǎn)繼承一些屬性,甚至是繼承一棵 DOM 樹進(jìn)來(lái)。

利用 Shadow DOM 的隔離性,我們就可以創(chuàng)造原生的 HTML 組件了。

實(shí)際上,瀏覽器已經(jīng)通過(guò) Shadow DOM 實(shí)現(xiàn)了一些組件了,只是我們使用過(guò)卻沒(méi)有察覺(jué)而已,這也是 Shadow DOM 封裝的組件的魅力所在:你只管寫一個(gè) HTML 標(biāo)簽,其他的交給我。(是不是有點(diǎn)像 React 的 JSX ???)

我們來(lái)看一看瀏覽器利用 Shadow DOM 實(shí)現(xiàn)的一個(gè)示例吧,那就是 video 標(biāo)簽:

 video controls src= ./video.mp4 width= 400 height= 300 /video 

我們來(lái)看一下瀏覽器渲染的結(jié)果:

3986383008-5bc7f0af9a354_articlex.png

等一下!不是說(shuō) Shadow DOM 嗎?這和普通 DOM 有啥區(qū)別???

在 Chrome 中,Elements 默認(rèn)是不顯示內(nèi)部實(shí)現(xiàn)的 Shadow DOM 節(jié)點(diǎn)的,需要在設(shè)置中啟用:

4254440722-5bc7f0c9331b5_articlex.png

3551890025-5bc7f0d516ea6_articlex.png

注:瀏覽器默認(rèn)隱藏自身的 Shadow DOM 實(shí)現(xiàn),但如果是用戶通過(guò)腳本創(chuàng)造的 Shadow DOM,是不會(huì)被隱藏的。

然后,我們就可以看到 video 標(biāo)簽的真面目了:

925947494-5bc7f0fadca05_articlex.png

在這里,你可完全像調(diào)試普通 DOM 一樣隨意調(diào)整 Shadow DOM 中的內(nèi)容(反正和普通 DOM 一樣,刷新一下就恢復(fù)了)。

我們可以看到上面這些 shadow DOM 中的節(jié)點(diǎn)大多都有 pseudo 屬性,根據(jù)這個(gè)屬性,你就可以在外面編寫 CSS 樣式來(lái)控制對(duì)應(yīng)的節(jié)點(diǎn)樣式了。比如,將上面這個(gè) pseudo= -webkit-media-controls-overlay-play-button 的 input 按鈕的背景色改為橙色:

video::-webkit-media-controls-overlay-play-button { background-color: orange;}

2935846866-5bc7f11a17342_articlex.png

由于 Shadow DOM 實(shí)際上也是 DOM 的一種,所以在 Shadow DOM 中還可以繼續(xù)嵌套 Shadow DOM,就像上面那樣。

瀏覽器中還有很多 Element 都使用了 Shadow DOM 的形式進(jìn)行封裝,比如 input 、 select 、 audio 等,這里就不一一展示了。

由于 Shadow DOM 的隔離性,所以即便是你在外面寫了個(gè)樣式:div { background-color: red !important; },Shadow DOM 內(nèi)部的 div 也不會(huì)受到任何影響

也就是說(shuō),寫樣式的時(shí)候,該用 id 的時(shí)候就用 id,該用 class 的時(shí)候就用 class,一個(gè)按鈕的 class 應(yīng)該寫成 .button 就寫成 .button。完全不用考慮當(dāng)前組件中的 id、class 可能會(huì)與其他組件沖突,你只要確保一個(gè)組件內(nèi)部不沖突就好——這很容易做到。

這解決了現(xiàn)在絕大多數(shù)的組件化框架都面臨的問(wèn)題:Element 的 class(className) 到底怎么寫?用前綴命名空間的形式會(huì)導(dǎo)致 class 名太長(zhǎng),像這樣:.header-nav-list-sublist-button-icon;而使用一些 CSS-in-JS 工具,可以創(chuàng)造一些唯一的 class 名稱,像這樣:.Nav__welcomeWrapper___lKXTg,這樣的名稱仍舊有點(diǎn)長(zhǎng),還帶了冗余信息。

ShadowRoot

ShadowRoot 是 Shadow DOM 下面的根,你可以把它當(dāng)做 DOM 中的 body 一樣看待,但是它不是 body ,所以你不能使用 body 上的一些屬性,甚至它不是一個(gè)節(jié)點(diǎn)。

你可以通過(guò) ShadowRoot 下面的 appendChild、querySelectorAll 之類的屬性或方法去操作整個(gè) Shadow DOM 樹。

對(duì)于一個(gè)普通的 Element,比如 p ,你可以通過(guò)調(diào)用它上面的 attachShadow 方法來(lái)創(chuàng)建一個(gè) ShadowRoot(還有一個(gè) createShadowRoot 方法,已經(jīng)過(guò)時(shí)不推薦使用),attachShadow 接受一個(gè)對(duì)象進(jìn)行初始化:{ mode: open },這個(gè)對(duì)象有一個(gè) mode 屬性,它有兩個(gè)取值: open 和 closed ,這個(gè)屬性是在創(chuàng)造 ShadowRoot 的時(shí)候需要初始化提供的,并在創(chuàng)建 ShadowRoot 之后成為一個(gè)只讀屬性。

mode: open 和 mode: closed 有什么區(qū)別呢?在調(diào)用 attachShadow 創(chuàng)建 ShadowRoot 之后,attachShdow 方法會(huì)返回 ShadowRoot 對(duì)象實(shí)例,你可以通過(guò)這個(gè)返回值去構(gòu)造整個(gè) Shadow DOM。當(dāng) mode 為 open 時(shí),在用于創(chuàng)建 ShadowRoot 的外部普通節(jié)點(diǎn)(比如 p )上,會(huì)有一個(gè) shadowRoot 屬性,這個(gè)屬性也就是創(chuàng)造出來(lái)的那個(gè) ShadowRoot,也就是說(shuō),在創(chuàng)建 ShadowRoot 之后,還是可以在任何地方通過(guò)這個(gè)屬性再得到 ShadowRoot,繼續(xù)對(duì)其進(jìn)行改造;而當(dāng) mode 為 closed 時(shí),你將不能再得到這個(gè)屬性,這個(gè)屬性會(huì)被設(shè)置為 null,也就是說(shuō),你只能在 attachShadow 之后得到 ShadowRoot 對(duì)象,用于構(gòu)造整個(gè) Shadow DOM,一旦你失去對(duì)這個(gè)對(duì)象的引用,你就無(wú)法再對(duì) Shadow DOM 進(jìn)行改造了。

可以從上面 Shadow DOM 的截圖中看到 #shadow-root (user-agent) 的字樣,這就是 ShadowRoot 對(duì)象了,而括號(hào)中的 user-agent 表示這是瀏覽器內(nèi)部實(shí)現(xiàn)的 Shadow DOM,如果使用通過(guò)腳本自己創(chuàng)建的 ShadowRoot,括號(hào)中會(huì)顯示為 open 或 closed 表示 Shadow DOM 的 mode。

3465801305-5bc7f119096b3_articlex.png

瀏覽器內(nèi)部實(shí)現(xiàn)的 user-agent 的 mode 為 closed,所以你不能通過(guò)節(jié)點(diǎn)的 ShadowRoot 屬性去獲得其 ShadowRoot 對(duì)象,也就意味著你不能通過(guò)腳本對(duì)這些瀏覽器內(nèi)部實(shí)現(xiàn)的 Shadow DOM 進(jìn)行改造。

HTML Template

有了 ShadowRoot 對(duì)象,我們可以通過(guò)代碼來(lái)創(chuàng)建內(nèi)部結(jié)構(gòu)了,對(duì)于簡(jiǎn)單的結(jié)構(gòu),也許我們可以直接通過(guò) document.createElement 來(lái)創(chuàng)建,但是稍微復(fù)雜一些的結(jié)構(gòu),如果全部都這樣來(lái)創(chuàng)建不僅麻煩,而且代碼可讀性也很差。當(dāng)然也可以通過(guò) ES6 提供的反引號(hào)字符串(const template = `......`;)配合 innerHTML 來(lái)構(gòu)造結(jié)構(gòu),利用反引號(hào)字符串中可以任意換行,并且 HTML 對(duì)縮進(jìn)并不敏感的特性來(lái)實(shí)現(xiàn)模版,但是這樣也是不夠優(yōu)雅,畢竟代碼里大段大段的 HTML 字符串并不美觀,即便是單獨(dú)抽出一個(gè)常量文件也是一樣。

這個(gè)時(shí)候就可以請(qǐng) HTML Template 出場(chǎng)了。我們可以在 html 文檔中編寫 DOM 結(jié)構(gòu),然后在 ShadowRoot 中加載過(guò)來(lái)即可。

HTML Template 實(shí)際上就是在 html 中的一個(gè) template 標(biāo)簽,正常情況下,這個(gè)標(biāo)簽下的內(nèi)容是不會(huì)被渲染的,包括標(biāo)簽下的 img、style、script 等都是不會(huì)被加載或執(zhí)行的。你可以在腳本中使用 getElementById 之類的方法得到 template 標(biāo)簽對(duì)應(yīng)的節(jié)點(diǎn),但是卻無(wú)法直接訪問(wèn)到其內(nèi)部的節(jié)點(diǎn),因?yàn)槟J(rèn)他們只是模版,在瀏覽器中表現(xiàn)為 #document-fragment,字面意思就是“文檔片段”,可以通過(guò)節(jié)點(diǎn)對(duì)象的 content 屬性來(lái)訪問(wèn)到這個(gè) document-fragment 對(duì)象。

2033065598-5bc7f18543064_articlex.png

通過(guò) document-fragment 對(duì)象,就可以訪問(wèn)到 template 內(nèi)部的節(jié)點(diǎn)了,通過(guò) document.importNode 方法,可以將 document-fragment 對(duì)象創(chuàng)建一份副本,然后可以使用一切 DOM 屬性方法替換副本中的模版內(nèi)容,最終將其插入到 DOM 或是 Shadow DOM 中。

 div id= div /div  template id= temp  div id= title /div  /template 
const template = document.getElementById( temp const copy = document.importNode(template.content, true);copy.getElementById( title ).innerHTML = Hello World! const div = document.getElementById( div const shadowRoot = div.attachShadow({ mode: closed });shadowRoot.appendChild(copy);

HTML Imports

有了 HTML Template,我們已經(jīng)可以方便地創(chuàng)造封閉的 Web 組件了,但是目前還有一些不完美的地方:我們必須要在 html 中定義一大批的 template ,每個(gè)組件都要定義一個(gè) template 。

此時(shí),我們就可以用到已經(jīng)被廢棄的 HTML Imports 了。雖然它已經(jīng)被廢棄了,但是未來(lái)會(huì)通過(guò) ES6 Modules 的形式再進(jìn)行支持,所以理論上也只是換個(gè)加載形式而已。

通過(guò) HTML Imports,我們可以將 template 定義在其他的 html 文檔中,然后再在需要的 html 文檔中進(jìn)行導(dǎo)入(當(dāng)然也可以通過(guò)腳本按需導(dǎo)入),導(dǎo)入后,我們就可以直接使用其中定義的模版節(jié)點(diǎn)了。

已經(jīng)廢棄的 HTML Imports 通過(guò) link 標(biāo)簽實(shí)現(xiàn),只要指定 rel= import 就可以了,就像這樣: link rel= import href= ./templates.html ,它可以接受 onload 和 onerror 事件以指示它已經(jīng)加載完成。當(dāng)然也可以通過(guò)腳本來(lái)創(chuàng)建 link 節(jié)點(diǎn),然后指定 rel 和 href 來(lái)按需加載。Import 成功后,在 link 節(jié)點(diǎn)上有一個(gè) import 屬性,這個(gè)屬性中存儲(chǔ)的就是 import 進(jìn)來(lái)的 DOM 樹啦,可以 querySelector 之類的,并通過(guò) cloneNode 或 document.importNode 方法創(chuàng)建副本后使用。

未來(lái)新的 HTML Imports 將會(huì)以 ES6 Module 的形式提供,可以在 JavaScript 中直接 import * as template from ./template.html ,也可以按需 import,像這樣:const template = await import( ./template.html 。不過(guò)目前雖然瀏覽器都已經(jīng)支持 ES6 Modules,但是在 import 其他模塊時(shí)會(huì)檢查服務(wù)端返回文件的 MIME 類型必須為 JavaScript 的 MIME 類型,否則不允許加載。

Custom Elements

有了上面的三個(gè)組件標(biāo)準(zhǔn),我們實(shí)際上只是對(duì) HTML 進(jìn)行拆分而已,將一個(gè)大的 DOM 樹拆成一個(gè)個(gè)相互隔離的小 DOM 樹,這還不是真正的組件。

要實(shí)現(xiàn)一個(gè)真正的組件,我們就需要用到 Custom Elements 了,就如它的名字一樣,它是用來(lái)定義原生組件的。

Custom Elements 的核心,實(shí)際上就是利用 JavaScript 中的對(duì)象繼承,去繼承 HTML 原生的 HTMLElement 類(或是具體的某個(gè)原生 Element 類,比如 HTMLButtonElement),然后自己編寫相關(guān)的生命周期函數(shù),處理成員屬性以及用戶交互的事件。

看起來(lái)這和現(xiàn)在的 React 很像,在 React 中,你可以這樣創(chuàng)造一個(gè)組件:class MyElement extends React.Component { ... },而使用原生 Custom Elements,你需要這樣寫:class MyElement extends HTMLElement { ... }。

Custom Elements 的生命周期函數(shù)并不多,但是足夠使用。這里我將 Custom Elements 的生命周期函數(shù)與 React 進(jìn)行一個(gè)簡(jiǎn)單的對(duì)比:

constructor(): 構(gòu)造函數(shù),用于初始化 state、創(chuàng)建 Shadow DOM、監(jiān)聽事件之類。

對(duì)應(yīng) React 中 Mounting 階段的大半部分,包括:constructor(props)、static getDerivedStateFromProps(props, state) 和 render()。

在 Custom Elements 中,constructor() 構(gòu)造函數(shù)就是其原本的含義:初始化,和 React 的初始化類似,但它沒(méi)有像 React 中那樣將其拆分為多個(gè)部分。在這個(gè)階段,組件僅僅是被創(chuàng)建出來(lái)(比如通過(guò) document.createElement()),但是還沒(méi)有插入到 DOM 樹中。

connectedCallback(): 組件實(shí)例已被插入到 DOM 樹中,用于進(jìn)行一些展示相關(guān)的初始化操作。

對(duì)應(yīng) React 中 Mounting 階段的最后一個(gè)生命周期:componentDidMount()。

在這個(gè)階段,組件已經(jīng)被插入到 DOM 樹中了,或是其本身就在 html 文件中寫好在 DOM 樹上了,這個(gè)階段一般是進(jìn)行一些展示相關(guān)的初始化,比如加載數(shù)據(jù)、圖片、音頻或視頻之類并進(jìn)行展示。

attributeChangedCallback(attrName, oldVal, newVal): 組件屬性發(fā)生變化,用于更新組件的狀態(tài)。

對(duì)應(yīng) React 中的 Updating 階段:static getDerivedStateFromProps(props, state)、shouldComponentUpdate(nextProps, nextState)、render()、getSnapshotBeforeUpdate(prevProps, prevState) 和 componentDidUpdate(prevProps, prevState, snapshot)。

當(dāng)組件的屬性(React 中的 props)發(fā)生變化時(shí)觸發(fā)這個(gè)生命周期,但是并不是所有屬性變化都會(huì)觸發(fā),比如組件的 class、style 之類的屬性發(fā)生變化一般是不會(huì)產(chǎn)生特殊交互的,如果所有屬性發(fā)生變化都觸發(fā)這個(gè)生命周期的話,會(huì)使得性能造成較大的影響。所以 Custom Elements 要求開發(fā)者提供一個(gè)屬性列表,只有當(dāng)屬性列表中的屬性發(fā)生變化時(shí)才會(huì)觸發(fā)這個(gè)生命周期函數(shù)。

這個(gè)屬性列表通過(guò)組件類上的一個(gè)靜態(tài)只讀屬性來(lái)聲明,在 ES6 Class 中使用一個(gè) getter 函數(shù)來(lái)實(shí)現(xiàn),只實(shí)現(xiàn) getter 而不實(shí)現(xiàn) setter,getter 返回一個(gè)常量,這樣就是只讀的了。像這樣:

class AwesomeElement extends HTMLElement { static get observedAttributes() { return [ awesome }

disconnectedCallback(): 組件被從 DOM 樹中移除,用于進(jìn)行一些清理操作。

對(duì)應(yīng) React 中的 Unmounting 階段:componentWillUnmount()。

adoptedCallback(): 組件實(shí)例從一個(gè)文檔被移動(dòng)到另一個(gè)文檔。

這個(gè)生命周期是原生組件獨(dú)有的,React 中沒(méi)有類似的生命周期。這個(gè)生命周期函數(shù)也并不常用到,一般在操作多個(gè) document 的時(shí)候會(huì)遇到,調(diào)用 document.adoptNode() 函數(shù)轉(zhuǎn)移節(jié)點(diǎn)所屬 document 時(shí)會(huì)觸發(fā)這個(gè)生命周期。

在定義了自定義組件后,我們需要將它注冊(cè)到 HTML 標(biāo)簽列表中,通過(guò) window.customElements.define() 函數(shù)即可實(shí)現(xiàn),這個(gè)函數(shù)接受兩個(gè)必須參數(shù)和一個(gè)可選參數(shù)。第一個(gè)參數(shù)是注冊(cè)的標(biāo)簽名,為了避免和 HTML 自身的標(biāo)簽沖突,Custom Elements 要求用戶自定義的組件名必須至少包含一個(gè)短杠 -,并且不能以短杠開頭,比如 my-element、awesome-button 之類都是可以的。第二個(gè)參數(shù)是注冊(cè)的組件的 class,直接將繼承的子類類名傳入即可,當(dāng)然也可以直接寫一個(gè)匿名類:

window.customElements.define( my-element , class extends HTMLElement {});

注冊(cè)之后,我們就可以使用了,可以直接在 html 文檔中寫對(duì)應(yīng)的標(biāo)簽,比如: my-element /my-element ,也可以通過(guò) document.createElement( my-element ) 來(lái)創(chuàng)建,用法與普通標(biāo)簽幾乎完全一樣。但要注意的是,雖然 html 標(biāo)準(zhǔn)中說(shuō)部分標(biāo)簽可以不關(guān)閉或是自關(guān)閉( br 或是 br / ),但是只有規(guī)定的少數(shù)幾個(gè)標(biāo)簽允許自關(guān)閉,所以,在 html 中寫 Custom Elements 的節(jié)點(diǎn)時(shí)必須帶上關(guān)閉標(biāo)簽。

由于 Custom Elements 是通過(guò) JavaScript 來(lái)定義的,而一般 js 文件都是通過(guò) script 標(biāo)簽外聯(lián)的,所以 html 文檔中的 Custom Elements 在 JavaScript 未執(zhí)行時(shí)是處于一個(gè)默認(rèn)的狀態(tài),瀏覽器默認(rèn)會(huì)將其內(nèi)容直接顯示出來(lái)。為了避免這樣的情況發(fā)生,Custom Elements 在被注冊(cè)后都會(huì)有一個(gè) :defined CSS 偽類而在注冊(cè)前沒(méi)有,所以我們可以通過(guò) CSS 選擇器在 Custom Elements 注冊(cè)前將其隱藏起來(lái),比如:

my-element:not(:defined) { display: none;}

或者 Custom Elements 也提供了一個(gè)函數(shù)來(lái)檢測(cè)指定的組件是否已經(jīng)被注冊(cè):customElements.whenDefined(),這個(gè)函數(shù)接受一個(gè)組件名參數(shù),并返回一個(gè) Promise,當(dāng) Promise 被 resolve 時(shí),就表示組件被注冊(cè)了。

這樣,我們就可以放心的在加載 Custom Elements 的 JavaScript 的 script 標(biāo)簽上使用 async 屬性來(lái)延遲加載了(當(dāng)然,如果是使用 ES6 Modules 形式的話默認(rèn)的加載行為就會(huì)和 defer 類似)。

1246191307-5bc7f1ac6b9fe_articlex.png

Custom Elements + Shadow DOM

使用 Custom Elements 來(lái)創(chuàng)建組件時(shí),通常會(huì)與 Shadow DOM 進(jìn)行結(jié)合,利用 Shadow DOM 的隔離性,就可以創(chuàng)造獨(dú)立的組件。

通常在 Custom Elements 的 constructor() 構(gòu)造函數(shù)中去創(chuàng)建 Shadow DOM,并對(duì) Shadow DOM 中的節(jié)點(diǎn)添加事件監(jiān)聽、對(duì)特定事件觸發(fā)原生 Events 對(duì)象。

正常編寫 html 文檔時(shí),我們可能會(huì)給 Custom Elements 添加一些子節(jié)點(diǎn),像這樣: my-element h1 Title /h1 p Content /p /my-element ,而我們創(chuàng)建的 Shadow DOM 又擁有其自己的結(jié)構(gòu),怎樣將這些子節(jié)點(diǎn)放置到 Shadow DOM 中正確的位置上呢?

在 React 中,這些子節(jié)點(diǎn)被放置在 props 的 children 中,我們可以在 render() 時(shí)選擇將它放在哪里。而在 Shadow DOM 中有一個(gè)特殊的標(biāo)簽: slot ,這個(gè)標(biāo)簽的用處就如同其字面意思,在 Shadow DOM 上放置一個(gè)“插槽”,然后 Custom Elements 的子節(jié)點(diǎn)就會(huì)自動(dòng)放置到這個(gè)“插槽”中了。

有時(shí)我們需要更加精確地控制子節(jié)點(diǎn)在 Shadow DOM 中的位置,而默認(rèn)情況下,所有子節(jié)點(diǎn)都會(huì)被放置在同一個(gè) slot 標(biāo)簽下,即便是你寫了多個(gè) slot 。那怎樣更精確地對(duì)子節(jié)點(diǎn)進(jìn)行控制呢?

默認(rèn)情況下, slot Fallback /slot 這樣的是默認(rèn)的 slot ,只有第一個(gè)默認(rèn)的 slot 會(huì)有效,將所有子節(jié)點(diǎn)全部放進(jìn)去,如果沒(méi)有可用的子節(jié)點(diǎn),將會(huì)顯示默認(rèn)的 Fallback 內(nèi)容(Fallback 可以是一棵子 DOM 樹)。

slot 標(biāo)簽有一個(gè) name 屬性,當(dāng)你提供 name 后,它將變?yōu)橐粋€(gè)“有名字的 slot ”,這樣的 slot 可以存在多個(gè),只要名字各不相同。此時(shí)他們會(huì)自動(dòng)匹配 Custom Elements 下帶 slot 屬性并且 slot 屬性與自身 name 相同的子節(jié)點(diǎn),像這樣

 template id= list  div  h1 Others /h1  slot /slot  /div  div  h1 Animals /h1  slot name= animal /slot  /div  div  h1 Fruits /h1  slot name= fruit /slot  /div  /template  my-list  div slot= animal Cat /div  div slot= fruit Apple /div  div slot= fruit Banana /div  div slot= other flower /div  div pencil /div  div slot= animal Dog /div  div slot= fruit peach /div  div red /div  /my-list 
class MyList extends HTMLElement { constructor() { super(); const root = this.attachShadow({ mode: open }); const template = document.getElementById( list  root.appendChild(document.importNode(template.content, true));customElements.define( my-list , MyList);

這樣就可以得到如圖所示的結(jié)構(gòu),#shadow-root (open) 表示這是一個(gè)開放的 Shadow DOM,下面的節(jié)點(diǎn)是直接從 template 中 clone 過(guò)來(lái)的,瀏覽器自動(dòng)在三個(gè) slot 標(biāo)簽下放置了幾個(gè)灰色的 div 節(jié)點(diǎn),實(shí)際上這些灰色的 div 節(jié)點(diǎn)表示的是到其真實(shí)節(jié)點(diǎn)的“引用”,鼠標(biāo)移動(dòng)到他們上會(huì)顯示一個(gè) reveal 鏈接,點(diǎn)擊這個(gè)鏈接即可跳轉(zhuǎn)至其真實(shí)節(jié)點(diǎn)。

1523399701-5bc7f1ce6b75a_articlex.png

這里我們可以看到,雖然 my-list 下的子節(jié)點(diǎn)是亂序放置的,但是只要是給定了 slot 屬性,就會(huì)被放置到正確的 slot 標(biāo)簽下。注意觀察其中有一個(gè) div slot= other flower /div ,這個(gè)節(jié)點(diǎn)由于指定了 slot= other ,但是卻找不到匹配的 slot 標(biāo)簽,所以它不會(huì)被顯示在結(jié)果中。

在為 Custom Elements 下的 Shadow DOM 設(shè)置樣式的時(shí)候,我們可以直接在 Shadow DOM 下放置 style 標(biāo)簽,也可以放置 link rel= stylesheet ,Shadow DOM 下的樣式都是局部的,所以不用擔(dān)心會(huì)影響到 Shadow DOM 的外部。并且由于這些樣式僅影響局部,所以對(duì)性能也有很大的提升。

在 Shadow DOM 內(nèi)部的樣式中,也有一些特定的選擇器,比如 :host 選擇器,代表著 ShadowRoot,這類似于普通 DOM 中的 :root,并且它可以與其他偽類組合使用,比如當(dāng)鼠標(biāo)在組件上時(shí)::host(:hover),當(dāng)組件擁有某個(gè) class 時(shí)::host(.awesome),當(dāng)組件擁有 disabled 屬性時(shí)::host([disabled])……但是 :host 是擁有繼承屬性的,所以如果在 Custom Elements 外部定義了某些樣式,將會(huì)覆蓋 :host 中的樣式,這樣就可以輕松地實(shí)現(xiàn)各式各樣的“主題風(fēng)格”了。

為了實(shí)現(xiàn)自定義主題,我們還可以使用 Shadow DOM 提供的 :host-context() 選擇器,這個(gè)選擇器允許檢查 Shadow DOM 的任何祖先節(jié)點(diǎn)是否包含指定選擇器。比如如果在最外層 DOM 的 html 或 body 上有一個(gè) class:.night,則 Shadow DOM 內(nèi)就可以使用 :host-context(.night) 來(lái)指定一個(gè)夜晚的主題。這樣可以實(shí)現(xiàn)主題樣式的繼承。

還有一種樣式的定義方式是利用 CSS 變量。我們?cè)?Shadow DOM 中使用變量來(lái)指定樣式,比如:background-color: var(--bg-colour, #0F0);,這樣就可以在 Shadow DOM 外面指定 --bg-colour 變量來(lái)設(shè)置樣式了,如果沒(méi)有指定變量,將使用默認(rèn)的樣式顏色 #0F0。

有時(shí)我們需要在 Shadow DOM 內(nèi)部使用完全自定義的樣式,比如字體樣式、字體大小,如果任由其繼承可能導(dǎo)致布局錯(cuò)亂,而每次在組件外面指定樣式又略顯麻煩,并且也破壞了組件的封裝性。所以,Shadow DOM 提供了一個(gè) all 屬性,只要指定 :host{ all: initial; } 就可以重置所有繼承的屬性。

Demo

Web Components 的 Demo 在網(wǎng)上已經(jīng)有很多了,這是我 2 年前初次接觸 ES6 與 Web Components 的時(shí)候?qū)懙囊粋€(gè) Demo:https://github.com/jinliming2/Calendar-js,一個(gè)日歷,當(dāng)時(shí)還是 v0 的規(guī)范,并且在 Firefox 下還存在會(huì)導(dǎo)致 Firefox 崩潰的 Bug(感覺(jué)是 Firefox 在實(shí)現(xiàn) Shadow DOM 時(shí)的 Bug)。目前這個(gè) Demo 已經(jīng)不能在 Firefox 下運(yùn)行了,因?yàn)?Firefox 已經(jīng)刪除了 v0 規(guī)范,開始實(shí)行 v1 標(biāo)準(zhǔn)了,所以近期我可能會(huì)重構(gòu)一下這個(gè) Demo。

以上就是你知道原生HTML組件是什么嗎?原生HTML組件的介紹的詳細(xì)內(nèi)容,html教程

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
亚洲综合网在线观看| 三级黄色片网站| 天堂视频在线免费观看| 欧美大胆a人体大胆做受| 亚洲欧美资源在线| 黄色成人免费观看| 久久国产精品视频| 北条麻妃在线视频观看| 老司机成人影院| 久久久久久久综合色一本| 久久精品一级爱片| 特级西西444www大胆免费看| 国产日韩欧美电影在线观看| 国产精品视频一区视频二区| 亚洲精品日韩激情在线电影| 草久视频在线观看| 日韩在线综合网| 污污美女网站| 2025韩国理伦片在线观看| 不卡av电影在线| 国产成人精品电影| 高跟丝袜欧美一区| 久草中文综合在线| 羞羞视频在线免费看| 国产精品乱看| 国产精品理人伦一区二区三区| 青青青伊人色综合久久| 五月天久久久久久| 国产91福利| 久久久久久国产精品久久| 国产九九在线| 欧美亚洲国产一卡| 欧美电影院免费观看| 亚洲欧洲一区二区在线播放| 欧美在线高清| 五月婷婷丁香综合网| 日本一本草久p| 欧美熟妇激情一区二区三区| 久久久久人妻一区精品色| 99精品中文字幕在线不卡| 影音成人av| 亚洲日本aⅴ片在线观看香蕉| 99国产麻豆精品| sis001欧美| 精品视频无码一区二区三区| 免费在线成人激情电影| 香蕉视频在线观看黄| 成人做爰www看视频软件| 成人动漫一区二区三区| 婷婷综合在线观看| 免费高清成人在线| 一区二区三区四区视频在线观看| 一本色道久久综合亚洲精品按摩| 超碰色偷偷男人的天堂| 福利一区二区在线| 欧美视频在线观看 亚洲欧| 成人一级片网站| 成年人视频在线网站| 日韩欧美精品一区二区三区| 任你躁av一区二区三区| 亚洲成人a级网| av黄色免费在线观看| 亚洲理论电影片| 2022国产麻豆剧果冻传媒剧情| 91精品久久久久久久久久久久久久| 欧美网站在线观看| 欧美另类专区| 97精品超碰一区二区三区| 国产精品伦一区二区三区级视频频| 亚洲色图图片网| 久久精品一区蜜桃臀影院| 国产精品www994| 秋霞网一区二区三区| 亚洲色图美女| 亚洲色图偷拍视频| 午夜精品久久久久久久99热| 亚洲巨乳在线| 在线视频观看国产| 日韩不卡一区| 九九热国产在线| 欧美最猛性xxxx高清| 日本成人免费网站| 欧美成人精品一区二区综合免费| 成人亚洲视频在线观看| 成人免费黄色在线| 久久久久免费观看| 国产超碰精品| 国产伦精品一区二区三区四区| 国产午夜视频在线播放| 日本美女视频网站| 成年网站在线| 无需播放器亚洲| 欧美激情图片区| 国产亚洲欧美aaaa| av电影一区二区三区| 9久久9毛片又大又硬又粗| 国产综合福利在线| 久久99久久99精品蜜柚传媒| 在线观看自拍| 成人美女视频在线看| 国产亚洲一本大道中文在线| 亚洲中文字幕无码一区| 激情五月播播久久久精品| 潘金莲一级淫片aaaaa免费看| 日韩国产成人无码av毛片| 国产99免费视频| 青青久精品观看视频最新| 91在线观看污| 久久精品视频亚洲| 成人福利视频在线看| 天堂在线一区二区三区| 亚洲第一天堂无码专区| 影音先锋欧美在线| 中文字幕高清在线免费播放| 欧美日韩国产一区精品一区| 久久久久久久美女| 久久九九久精品国产免费直播| 在线中文视频| 88国产精品欧美一区二区三区| 亚洲伦伦在线| 在线观看xxxxvideo| 特级西西人体www高清大胆| 欧美精品一二三区| 日韩中文视频免费在线观看| 亚洲精品怡红院| 女人让男人操自己视频在线观看| 精品自拍视频在线观看| 国产偷窥女洗浴在线观看亚洲| 国产毛片精品久久| 国产成人精品免费看| 亚洲人成在线免费观看| 天海翼精品一区二区三区| 亚洲警察之高压线| 国产精品污网站| xxxx欧美18另类的高清| 国内精品久久久久久不卡影院| 在线不卡中文字幕播放| 99精品人妻无码专区在线视频区| 亚洲精品一二三区| 欧洲大片精品免费永久看nba| 日韩精品一二三| 婷婷视频一区二区三区| 九九热久久66| 综合干狼人综合首页| 俄罗斯xxxx性全过程| 成年人在线观看av| 年下总裁被打光屁股sp| 69堂免费精品视频在线播放| 日韩av片免费在线观看| 91极品视觉盛宴| 羞羞视频在线观看一区二区| 亚洲天堂男人天堂女人天堂| 欧美三级超在线视频| 51漫画成人app入口| 九九精品视频在线观看九九| 国产精品视频一区二区三| 小黄鸭精品aⅴ导航网站入口| 免费三级欧美电影| 97色在线播放视频| 97精品久久久久中文字幕| 精品99一区二区三区| 懂色av成人一区二区三区| 一卡二卡三卡四卡| 浪潮色综合久久天堂| 欧美日韩中文字幕在线| 日韩av视屏| 在线观看免费视频一区| 日本视频免费观看| 99pao成人国产永久免费视频| 日韩三级久久久| 999av小视频在线| jizzjizzjizzjizz日本老师| 青青草激情视频| 国产一区精品福利| 色135综合网| 国产精品一区二区美女视频免费看| 国产精品偷伦视频免费观看国产| 久久久久久亚洲精品不卡4k岛国| 亚洲午夜精品久久久久久高潮| 亚洲乱码日产精品bd| 人交獸av完整版在线观看| 黄色影片网址| 精品白丝av| 精品人伦一区二区三区蜜桃免费| xxxx性bbbb欧美野外| 中文字幕一区免费在线观看| 亚洲精品电影| 日本肉肉一区| 超碰97在线人人| 99国产精品久久一区二区三区| 欧美gay1069大粗吊| 午夜av在线播放| 尤物网站在线| 久久精品久久久久电影| 在线视频国产福利| 午夜精品久久久久久久无码| 忘忧草在线www成人影院| 国产sm主人调教女m视频| bestiality新另类大全| 亚洲视频在线免费观看| 亚洲欧美在线一区| 日韩一区二区三免费高清| 五月婷中文字幕| 欧美专区视频| 久久久久成人网站| wwwww黄色| av成人激情| 欧美午夜宅男影院在线观看| 91福利视频导航| 国产美女免费无遮挡| 777午夜精品视频在线播放| 日本一区二区三区网站| 久久婷婷蜜乳一本欲蜜臀| 免费激情视频网站| 日本一极黄色片| 91在线直播亚洲| 亚洲欧洲美洲综合色网| 做爰高潮hd色即是空| 国产亚洲视频在线| 久久777国产线看观看精品| 色播视频在线观看| 日韩在线观看成人| 天天干天天草| 亚洲精品四区| 成人18免费| 成人一级毛片| 国内精品免费视频精选在线观看| 成人三级在线| seerx性欧美巨大| 黄色网址入口| 波多野结衣一二三区| 五月天中文字幕在线| 伊人精品视频在线观看| 日本久久中文字幕| 97久久天天综合色天天综合色hd| 欧美在线国产精品| 久久综合九色综合久| 成人网页在线免费观看| 亚洲欧美国产高清va在线播| 香蕉视频官网在线观看日本一区二区| 日韩欧美亚洲国产另类| 伊人国产在线看一| 十八禁视频网站在线观看| 国产亚洲精品精品国产亚洲综合| 四虎永久成年免费影院| 国产主播福利在线| 精品国产乱码久久久久久免费| 亚洲精品视频一区二区三区| 国产精品久久久久9999赢消| 精品无码国模私拍视频| 色哟哟在线观看| 91av在线国产| 精品无人乱码一区二区三区的优势| 久久美女视频| 1024亚洲合集| 在线观看视频一区二区三区| 欧美一级精品片在线看| 亚洲国产日韩综合一区| 黄色录像a级片| 国产字幕在线观看| 麻豆视频久久| 九一精品在线观看| 国产二区国产一区在线观看| 一区二区在线影院| 欧美日韩一区二区三区视频播放| 一区二区三区欧美激情| 久久国产精品免费| 青青草成人av| 草莓视频成人appios| 9.1人成人免费视频网站| 欧美h视频在线观看| 欧美孕妇与黑人巨交| 激情视频免费网站| 中文字幕亚洲激情| 中文字幕一区二区三区乱码在线| 国产裸体永久免费无遮挡| 男女激情无遮挡| 久久99精品久久| 在线视频免费在线观看一区二区| 久久精品国产亚洲精品2020| 国产成人自拍视频在线| 激情视频在线观看一区二区三区| 96pao国产成视频永久免费| 日本一区二区成人在线| 欧美91福利在线观看| 四虎在线观看| 亚洲精品视频91| 国产成人a视频高清在线观看| 超污黄色软件| 亚洲一级免费毛片| 亚洲欧洲在线看| 午夜精品亚洲一区二区三区嫩草| 亚洲欧美日韩国产成人| 国产二区视频在线观看| 好吊的妞视频这里都有| 手机福利小视频在线播放| 国产精品视频第一页| 色吊一区二区三区| 亚洲视频在线观看免费| 日韩制服丝袜av| 国产在线拍揄自揄拍| 欧美伊人亚洲伊人色综合动图| 一区精品在线| 中文字幕中文字幕在线中高清免费版| 久久综合九色综合久99| 久久久福利影院| 欧美一区欧美二区| 三年片免费观看大全| 成年人深夜福利| 成人综合婷婷国产精品久久| 中国jizz妇女jizz妇女| 亚洲成人一区二区| 久久久久久尹人网香蕉| 青柠在线影院观看日本| 午夜美女久久久久爽久久| 欧美激情精品久久久久久免费印度| 九九热线视频只有这里最精品| 欧美在线观看网址综合| 99久久国产宗和精品1上映| 久久久久久久91| 国产视频精品一区二区三区| 国模私拍视频在线| 亚洲精品一卡二卡三卡四卡| 欧美影院久久久| 国产亚洲精品女人久久久久久| 欧美天天综合网| 久久青青草视频| 又黄又爽在线免费观看|