自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Web Components是個(gè)什么樣的東西

開(kāi)發(fā) 前端
Web Components 包括了四個(gè)部分:Custom Elements、HTML Imports、HTML Templates、Shadow DOM這四部分有機(jī)地組合在一起,才是 Web Components。

[[170777]]

前端組件化這個(gè)主題相關(guān)的內(nèi)容已經(jīng)火了很久很久,angular 剛出來(lái)時(shí)的 Directive 到 angular2 的 components,還有 React 的components 等等,無(wú)一不是前端組件化的一種實(shí)現(xiàn)和探索,但是提上議程的 Web Components 標(biāo)準(zhǔn)是個(gè)怎樣的東西,相關(guān)的一些框架或者類(lèi)庫(kù),如 React,Angular2,甚至是 x-tag,polymer 現(xiàn)在實(shí)現(xiàn)的組件化的東西和 Web Components 標(biāo)準(zhǔn)差別在哪里?我花時(shí)間努力地把現(xiàn)有的 W3C Web Components 文檔看了下,然后堅(jiān)強(qiáng)地寫(xiě)下這些記錄。

首先我們需要知道,Web Components 包括了四個(gè)部分:

  • Custom Elements
  • HTML Imports
  • HTML Templates
  • Shadow DOM

這四部分有機(jī)地組合在一起,才是 Web Components。

可以用自定義的標(biāo)簽來(lái)引入組件是前端組件化的基礎(chǔ),在頁(yè)面引用 HTML 文件和 HTML 模板是用于支撐編寫(xiě)組件視圖和組件資源管理,而 Shadow DOM 則是隔離組件間代碼的沖突和影響。

下邊分別是每一部分的筆記內(nèi)容。

Custom Elements

概述

Custom Elements 顧名思義,是提供一種方式讓開(kāi)發(fā)者可以自定義 HTML 元素,包括特定的組成,樣式和行為。支持 Web Components 標(biāo)準(zhǔn)的瀏覽器會(huì)提供一系列 API 給開(kāi)發(fā)者用于創(chuàng)建自定義的元素,或者擴(kuò)展現(xiàn)有元素。

這一項(xiàng)標(biāo)準(zhǔn)的草案還處于不穩(wěn)定的狀態(tài),時(shí)有更新,API 還會(huì)有所變化,下邊的筆記以 Cutsom Elements 2016.02.26 這個(gè)版本為準(zhǔn),因?yàn)樵谧钚碌?chrome 瀏覽器已經(jīng)是可以工作的了,這樣可以使用 demo 來(lái)做嘗試,最后我會(huì)再簡(jiǎn)單寫(xiě)一下最新文檔和這個(gè)的區(qū)別。

registerElement

首先,我們可以嘗試在 chrome 控制臺(tái)輸入 HTMLInputElement,可以看到是有這么一個(gè)東西的,這個(gè)理解為 input DOM 元素實(shí)例化時(shí)的構(gòu)造函數(shù),基礎(chǔ)的是 HTMLElement。

Web Components 標(biāo)準(zhǔn)提出提供這么一個(gè)接口:

  1. document.registerElement('x-foo', { 
  2.   prototype: Object.create(HTMLElement.prototype, { 
  3.     createdCallback: {       
  4.       value: function() { ... } 
  5.     }, 
  6.     ... 
  7.   }) 
  8. })  

你可以使用 document.registerElement 來(lái)注冊(cè)一個(gè)標(biāo)簽,標(biāo)準(zhǔn)中為了提供 namesapce 的支持,防止沖突,規(guī)定標(biāo)簽類(lèi)型(也可以理解為名字)需要使用 - 連接。同時(shí),不能是以下這一些:

  • annotation-xml
  • color-profile
  • font-face
  • font-face-src
  • font-face-uri
  • font-face-format
  • font-face-name
  • missing-glyph

第二個(gè)參數(shù)是標(biāo)簽相關(guān)的配置,主要是提供一個(gè) prototype,這個(gè)原型對(duì)象是以 HTMLElement 等的原型為基礎(chǔ)創(chuàng)建的對(duì)象。然后你便可以在 HTML 中去使用自定義的標(biāo)簽。如:

  1. <div> 
  2.   <x-foo></x-foo> 
  3. </div>  

是不是嗅到了 React 的味道?好吧,React 說(shuō)它自己主要不是做這個(gè)事情的。

生命周期和回調(diào)

在這個(gè) API 的基礎(chǔ)上,Web Components 標(biāo)準(zhǔn)提供了一系列控制自定義元素的方法。我們來(lái)一一看下:

一個(gè)自定義元素會(huì)經(jīng)歷以下這些生命周期:

  • 注冊(cè)前創(chuàng)建
  • 注冊(cè)自定義元素定義
  • 在注冊(cè)后創(chuàng)建元素實(shí)例
  • 元素插入到 document 中
  • 元素從 document 中移除
  • 元素的屬性變化時(shí)

這個(gè)是很重要的內(nèi)容,開(kāi)發(fā)者可以在注冊(cè)新的自定義元素時(shí)指定對(duì)應(yīng)的生命周期回調(diào)來(lái)為自定義元素添加各種自定義的行為,這些生命周期回調(diào)包括了:

createdCallback

自定義元素注冊(cè)后,在實(shí)例化之后會(huì)調(diào)用,通常多用于做元素的初始化,如插入子元素,綁定事件等。

  • attachedCallback
    元素插入到 document 時(shí)觸發(fā)。
  • detachedCallback
  • 元素從 document 中移除時(shí)觸發(fā),可能會(huì)用于做類(lèi)似 destroy 之類(lèi)的事情。
  • attributeChangedCallback
    元素屬性變化時(shí)觸發(fā),可以用于從外到內(nèi)的通信。外部通過(guò)修改元素的屬性來(lái)讓內(nèi)部獲取相關(guān)的數(shù)據(jù)并且執(zhí)行對(duì)應(yīng)的操作。

這個(gè)回調(diào)在不同情況下有對(duì)應(yīng)不同的參數(shù):

  • 設(shè)置屬性時(shí),參數(shù)列表是:屬性名稱(chēng),null,值,命名空間
  • 修改屬性時(shí),參數(shù)列表是:屬性名稱(chēng),舊值,新值,命名空間
  • 刪除屬性時(shí),參數(shù)列表是:屬性名稱(chēng),舊值,null,命名空間

好了,就上邊了解到的基礎(chǔ)上,假設(shè)我們要?jiǎng)?chuàng)建一個(gè)自定義的 button-hello 按鈕,點(diǎn)擊時(shí)會(huì) alert('hello world'),代碼如下:

  1. document.registerElement('button-hello', { 
  2.   prototype: Object.create(HTMLButtonElement.prototype, { 
  3.     createdCallback: { 
  4.       value: function createdCallback() { 
  5.         this.innerHTML = '<button>hello world</button>' 
  6.         this.addEventListener('click', () => { 
  7.           alert('hello world'
  8.         }) 
  9.       } 
  10.     } 
  11.   }) 
  12. })  

要留意上述代碼執(zhí)行之后才能使用 <button-hello></button-hello>

擴(kuò)展原有元素

其實(shí),如果我們需要一個(gè)按鈕,完全不需要重新自定義一個(gè)元素,Web Components 標(biāo)準(zhǔn)提供了一種擴(kuò)展現(xiàn)有標(biāo)簽的方式,把上邊的代碼調(diào)整一下: 

  1. document.registerElement('button-hello', { 
  2.   prototype: Object.create(HTMLButtonElement.prototype, { 
  3.     createdCallback: { 
  4.       value: function createdCallback() { 
  5.         this.addEventListener('click', () => { 
  6.           alert('hello world'
  7.         }) 
  8.       } 
  9.     } 
  10.   }), 
  11.   extends: 'button' 
  12. })  

然后在 HTML 中要這么使用:

  1. <button is="button-hello">hello world</button> 

使用 is 屬性來(lái)聲明一個(gè)擴(kuò)展的類(lèi)型,看起來(lái)也蠻酷的。生命周期和自定義元素標(biāo)簽的保持一致。

當(dāng)我們需要多個(gè)標(biāo)簽組合成新的元素時(shí),我們可以使用自定義的元素標(biāo)簽,但是如果只是需要在原有的 HTML 標(biāo)簽上進(jìn)行擴(kuò)展的話,使用 is 的這種元素?cái)U(kuò)展的方式就好。

原有的 createElement 和 createElementNS,在 Web Components 標(biāo)準(zhǔn)中也擴(kuò)展成為支持元素?cái)U(kuò)展,例如要?jiǎng)?chuàng)建一個(gè) button-hello:

  1. const hello = document.createElement('button''button-hello'

標(biāo)準(zhǔn)文檔中還有很多細(xì)節(jié)上的內(nèi)容,例如接口的參數(shù)說(shuō)明和要求,回調(diào)隊(duì)列的實(shí)現(xiàn)要求等,這些更多是對(duì)于實(shí)現(xiàn)這個(gè)標(biāo)準(zhǔn)的瀏覽器開(kāi)發(fā)者的要求,這里不做詳細(xì)描述了,內(nèi)容很多,有興趣的自行查閱:Cutsom Elements 2016.02.26。

和最新版的區(qū)別

前邊我提到說(shuō)文檔的更新變化很快,截止至我寫(xiě)這個(gè)文章的時(shí)候,最新的文檔是這個(gè):Custom Elements 2016.07.21。

細(xì)節(jié)不做描述了,講講我看到的最大變化,就是向 ES6 靠攏。大致有下邊三點(diǎn):

  • 從原本的擴(kuò)展 prototype 來(lái)定義元素調(diào)整為建議使用 class extends 的方式
  • 注冊(cè)自定義元素接口調(diào)整,更加方便使用,傳入 type 和 class 即可
  • 生命周期回調(diào)調(diào)整,createdCallback 直接用 class 的 constructor

前兩個(gè)點(diǎn),我們直接看下代碼,原本的代碼按照新的標(biāo)準(zhǔn),應(yīng)該調(diào)整為:

  1. class ButtonHelloElement extends HTMLButtonElement { 
  2.   constructor() { 
  3.     super() 
  4.  
  5.     this.addEventListener('click', () => { 
  6.       alert('hello world'
  7.     }) 
  8.   } 
  9.  
  10. customElements.define('button-hello', ButtonHelloElement, { extends: 'button' })  

從代碼上看會(huì)感覺(jué)更加 OO,編寫(xiě)上也比原本要顯得方便一些,原本的生命周期回調(diào)是調(diào)整為新的:

  • constructor in class 作用相當(dāng)于原本的 createdCallback
  • connectedCallback 作用相當(dāng)于 attachedCallback
  • disconnectedCallback 作用相當(dāng)于 detachedCallback
  • adoptedCallback 使用 document.adoptNode(node) 時(shí)觸發(fā)
  • attributeChangedCallback 和原本保持一致

connect 事件和插入元素到 document 有些許區(qū)別,主要就是插入元素到 document 時(shí),元素狀態(tài)會(huì)變成 connected,這時(shí)會(huì)觸發(fā) connectedCallback,disconnect 亦是如此。

HTML Imports

概述

HTML Imports 是一種在 HTMLs 中引用以及復(fù)用其他的 HTML 文檔的方式。這個(gè) Import 很漂亮,可以簡(jiǎn)單理解為我們常見(jiàn)的模板中的include 之類(lèi)的作用。

我們最常見(jiàn)的引入一個(gè) css 文件的方式是:

  1. <link rel="stylesheet" href="/css/master.css"

Web Components 現(xiàn)在提供多了一個(gè)這個(gè):

  1. <link rel="import" href="/components/header.html"

HTMLLinkElement

原本的 link 標(biāo)簽在添加了 HTML Import 之后,多了一個(gè)只讀的 import 屬性,當(dāng)出現(xiàn)下邊兩種情況時(shí),這個(gè)屬性為 null:

  • 該 link 不是用來(lái) import 一個(gè) HTML 的。
  • 該 link 元素不在 document 中。

否則,這個(gè)屬性會(huì)返回一個(gè)表示引入的 HTML 文件的文檔對(duì)象,類(lèi)似于 document。比如說(shuō),在上邊的代碼基礎(chǔ)上,可以這樣做:

  1. const link = document.querySelector('link[rel=import]'
  2. const header = link.import; 
  3.  
  4. const pulse = header.querySelector('div.logo');  

阻塞式

我們要知道的是,默認(rèn)的 link 加載是阻塞式的,除非你給他添加一個(gè) async 標(biāo)識(shí)。

阻塞式從某種程度上講是有必要的,當(dāng)你 improt 的是一個(gè)完整的自定義組件并且需要在主 HTML 中用標(biāo)簽直接使用時(shí),非阻塞的就會(huì)出現(xiàn)錯(cuò)誤了,因?yàn)闃?biāo)簽還沒(méi)有被注冊(cè)。

document

有一點(diǎn)值得留意的是,在 import 的 HTML 中,我們編寫(xiě)的 script 里邊的 document 是指向 import 這個(gè) HTML 的主 HTML 的 document。

如果我們要獲取 import 的 HTML 的 document 的話,得這么來(lái):

  1. const d = document.currentScript.ownerDocument 

這樣設(shè)計(jì)是因?yàn)?import 進(jìn)來(lái)的 HTML 需要用到主 HTML 的 document。例如我們上邊提到的 registerElement。

在一個(gè)被 import 的 HTML 文件中使用下邊三個(gè)方法會(huì)拋出一個(gè) InvalidStateError 異常:

  • document.open()
  • document.write()
  • document.close()

對(duì)于 HTML Import,標(biāo)準(zhǔn)文檔中還有很大一部分內(nèi)容是關(guān)于多個(gè)依賴(lài)加載的處理算法的,在這里就不詳述了,有機(jī)會(huì)的話找時(shí)間再開(kāi)篇談,這些內(nèi)容是需要瀏覽器去實(shí)現(xiàn)的。

HTML Templates

概述

這個(gè)東西很簡(jiǎn)單,用過(guò) handlebars 的人都知道有這么一個(gè)東西:

  1. <script id="template" type="text/x-handlebars-template"
  2.   ... 
  3. </script>  

其他模板引擎也有類(lèi)似的東西,那么 HTML Templates 便是把這個(gè)東西官方標(biāo)準(zhǔn)化,提供了一個(gè) template 標(biāo)簽來(lái)存放以后需要但是暫時(shí)不渲染的 HTML 代碼。

以后可以這么寫(xiě)了:

  1. <template id="template"
  2.   ... 
  3. </template>  

接口和應(yīng)用

template 元素有一個(gè)只讀的屬性 content,用于返回這個(gè) template 里邊的內(nèi)容,返回的結(jié)果是一個(gè) DocumentFragment。

具體是如何使用的,直接參考官方給出的例子:

  1. <!doctype html> 
  2. <html lang="en"
  3.   <head> 
  4.     <title>Homework</title> 
  5.   <body> 
  6.     <template id="template"><p>Smile!</p></template> 
  7.     <script> 
  8.       let num = 3; 
  9.       const fragment = document.getElementById('template').content.cloneNode(true); 
  10.       while (num-- > 1) { 
  11.         fragment.firstChild.before(fragment.firstChild.cloneNode(true)); 
  12.         fragment.firstChild.textContent += fragment.lastChild.textContent; 
  13.       } 
  14.       document.body.appendChild(fragment); 
  15.     </script> 
  16. </html>  

使用 DocumentFragment 的 clone 方法以 template 里的代碼為基礎(chǔ)創(chuàng)建一個(gè)元素節(jié)點(diǎn),然后你便可以操作這個(gè)元素節(jié)點(diǎn),最后在需要的時(shí)候插入到 document 中特定位置便可以了。

Template 相關(guān)的東西不多,而且它現(xiàn)在已經(jīng)是納入生效的 標(biāo)準(zhǔn)文檔 中了。

我們接下來(lái)看看重磅的 Shadow DOM。

Shadow DOM

概述

Shadow DOM 好像提出好久了,最本質(zhì)的需求是需要一個(gè)隔離組件代碼作用域的東西,例如我組件代碼的 CSS 不能影響其他組件之類(lèi)的,而 iframe 又太重并且可能有各種奇怪問(wèn)題。

可以這么說(shuō),Shadow DOM 旨在提供一種更好地組織頁(yè)面元素的方式,來(lái)為日趨復(fù)雜的頁(yè)面應(yīng)用提供強(qiáng)大支持,避免代碼間的相互影響。

看下在 chrome 它會(huì)是咋樣的: 

我們可以通過(guò) createShadowRoot() 來(lái)給一個(gè)元素節(jié)點(diǎn)創(chuàng)建 Shadow Root,這些元素類(lèi)型必須是下邊列表的其中一個(gè),否則會(huì)拋出 NotSupportedError 異常。

  • 自定義的元素
  • article
  • aside
  • blockquote
  • body
  • div
  • header, footer
  • h1, h2, h3, h4, h5, h6
  • nav
  • p
  • section
  • span

createShadowRoot() 是現(xiàn)在 chrome 實(shí)現(xiàn)的 API,來(lái)自文檔:https://www.w3.org/TR/2014/WD...。最新的文檔 API 已經(jīng)調(diào)整為 attachShadow()。

返回的 Shadow Root 對(duì)象從 DocumentFragment 繼承而來(lái),所以可以使用相關(guān)的一些方法,例如shadowRoot.getElementById('id') 來(lái)獲取 Shadow DOM 里邊的元素。

簡(jiǎn)單的使用如下:

  1. const div = document.getElementById('id'
  2. const shadowRoot = div.createShadowRoot() 
  3. const span = document.createElement('span'
  4.  
  5. span.textContent = 'hello world' 
  6. shadowRoot.appendChild(span)  

在這里,我把這個(gè) div 成為是這個(gè) Shadow DOM 的 宿主元素,下邊的內(nèi)容會(huì)延續(xù)使用這個(gè)稱(chēng)呼。

Shadow DOM 本身就為了代碼隔離而生,所以在 document 上使用 query 時(shí),是沒(méi)法獲取到 Shadow DOM 里邊的元素的,需要在 Shadow Root 上做 query 才行。

在這里附上一個(gè)文檔,里邊有詳細(xì)的關(guān)于新的標(biāo)準(zhǔn)和現(xiàn)在 blink 引擎實(shí)現(xiàn)的 Shadow DOM 的區(qū)別,官方上稱(chēng)之為 v0 和 v1:Shadow DOM v1 in Blink。

API

Shadow Root 除了從 DocumentFragment 繼承而來(lái)的屬性和方法外,還多了另外兩個(gè)屬性:

  • host 只讀屬性,用來(lái)獲取這個(gè) Shadow Root 所屬的元素
  • innerHTML 用來(lái)獲取或者設(shè)置里邊的 HTML 字符串,和我們常用的 element.innerHTML 是一樣的

另外,在最新的標(biāo)準(zhǔn)文檔中,元素除了上邊提到的 attachShadow 方法之外,還多了三個(gè)屬性:

  • assignedSlot 只讀,這個(gè)元素如果被分配到了某個(gè) Shadow DOM 里邊的 slot,那么會(huì)返回這個(gè)對(duì)應(yīng)的 slot 元素
  • slot 元素的 slot 屬性,用來(lái)指定 slot 的名稱(chēng)
  • shadowRoot 只讀,元素下面對(duì)應(yīng)的 Shadow Root 對(duì)象

slot 是什么?接著看下邊的內(nèi)容,看完下一節(jié)的最后一部分就會(huì)明白上述內(nèi)容和 slot 相關(guān)的兩個(gè) API 有什么作用。

slot

slot 提供了在使用自定義標(biāo)簽的時(shí)候可以傳遞子模板給到內(nèi)部使用的能力,可以簡(jiǎn)單看下 Vue 的一個(gè)例子。

我們先來(lái)看下現(xiàn)在 chrome 可以跑的 v0 版本,這一個(gè)版本是提供了一個(gè) content 標(biāo)簽,代表了一個(gè)占位符,并且有一個(gè) select 屬性用來(lái)指定使用哪些子元素。

  1. <!-- component input-toggle template --> 
  2. <input type="checkbox"></input> 
  3. <content select=".span"></content>  

自定義的元素里邊的子元素代碼是這樣的:

  1. <input-toggle name="hello"
  2.   <span>hello</span> 
  3.   <span class="span">test</span> 
  4. </input-toggle>  

那么展現(xiàn)的結(jié)果會(huì)和下邊的代碼是一樣的:

  1. <input-toggle name="hello"
  2.   <input type="checkbox"></input> 
  3.   <span class="span">test</span> 
  4. </input-toggle>  

這里只是說(shuō)展現(xiàn)結(jié)果,實(shí)際上,input-toggle 里邊應(yīng)該是創(chuàng)建了一個(gè) Shadow DOM,然后 content 標(biāo)簽引用了目標(biāo)的 span 內(nèi)容,在 chrome 看是這樣的: 

 然后,是最新標(biāo)準(zhǔn)中的 slot 使用方式,直接上例子代碼:

  1. <!-- component input-toggle template --> 
  2. <input type="checkbox"></input> 
  3. <slot name="text"></slot>  

在自定義的元素標(biāo)簽是這么使用 slot 的:

  1. <input-toggle name="hello"
  2.   <input type="checkbox"></input> 
  3.   <span class="span" slot="text">test</span> 
  4. </input-toggle> 

 

通過(guò) slot="text" 的屬性來(lái)讓元素內(nèi)部的 slot 占位符可以引用到這個(gè)元素,多個(gè)元素使用這個(gè)屬性也是可以的。這樣子我們便擁有了使用標(biāo)簽是從外部傳 template 給到自定義元素的內(nèi)部去使用,而且具備指定放在那里的能力。

CSS 相關(guān)

因?yàn)橛?Shadow DOM 的存在,所以在 CSS 上又添加了很多相關(guān)的東西,其中一部分還是屬于討論中的草案,命名之類(lèi)的可能會(huì)有變更,下邊提及的內(nèi)容主要來(lái)自文檔:Shadow DOM in CSS scoping 1,很多部分在 chrome 是已經(jīng)實(shí)現(xiàn)的了,有興趣可以寫(xiě) demo 試試。

因?yàn)?Shadow DOM 很大程度上是為了隔離樣式作用域而誕生的,主文檔中的樣式規(guī)則不對(duì) Shadow DOM 里的子文檔生效,子文檔中的樣式規(guī)則也不影響外部文檔。

但不可避免的,在某些場(chǎng)景下,我們需要外部可以控制 Shadow DOM 中樣式,如提供一個(gè)組件給你,有時(shí)候你會(huì)希望可以自定義它內(nèi)部的一些樣式,同時(shí),Shadow DOM 中的代碼有時(shí)候可能需要能夠控制其所屬元素的樣式,甚至,組件內(nèi)部可以定義上邊提到的通過(guò) slot 傳遞進(jìn)來(lái)的 HTML 的樣式。所以呢,是的,CSS 選擇器中添加了幾個(gè)偽類(lèi),我們一一來(lái)看下它們有什么作用。

在閱讀下邊描述的時(shí)候,請(qǐng)留意一下選擇器的代碼是在什么位置的,Shadow DOM 內(nèi)部還是外部。

:host 用于在 Shadow DOM 內(nèi)部選擇到其宿主元素,當(dāng)它不是在 Shadow DOM 中使用時(shí),便匹配不到任意元素。

在 Shadow DOM 中的 * 選擇器是無(wú)法選擇到其宿主元素的。

:host( <selector> ) 括號(hào)中是一個(gè)選擇器,這個(gè)可以理解為是一個(gè)用于兼容在主文檔和 Shadow DOM 中使用的方法,當(dāng)這個(gè)選擇器在 Shadow DOM 中時(shí),會(huì)匹配到括號(hào)中選擇器對(duì)應(yīng)的宿主元素,如果不是,則匹配括號(hào)中選擇器能夠匹配到的元素。

文檔中提供了一個(gè)例子:

  1. <x-foo class="foo"
  2.   <"shadow tree"
  3.     <div class="foo">...</div> 
  4.   </> 
  5. </x-foo> 

 

在這個(gè) shadow tree 內(nèi)部的樣式代碼中,會(huì)有這樣的結(jié)果:

  • :host 匹配 <x-foo> 元素
  • x-foo 匹配不到元素
  • .foo 只匹配到 <div> 元素
  • .foo:host 匹配不到元素
  • :host(.foo) 匹配 <x-foo> 元素

:host-context( <selector> ),用于在 Shadow DOM 中來(lái)檢測(cè)宿主元素的父級(jí)元素,如果宿主元素或者其祖先元素能夠被括號(hào)中的選擇器匹配到的話,那么這個(gè)偽類(lèi)選擇器便匹配到這個(gè) Shadow DOM 的宿主元素。個(gè)人理解是用于在宿主元素外部元素滿(mǎn)足一定的條件時(shí)添加樣式。

::shadow 這個(gè)偽類(lèi)用于在 Shadow DOM 外部匹配其內(nèi)部的元素,而 /deep/ 這個(gè)標(biāo)識(shí)也有同樣的作用,我們來(lái)看一個(gè)例子:

  1. <x-foo> 
  2.    <"shadow tree"
  3.      <div> 
  4.        <span id="not-top">...</span> 
  5.      </div> 
  6.      <span id="top">...</span> 
  7.    </> 
  8.  </x-foo> 

 

對(duì)于上述這一段代碼的 HTML 結(jié)構(gòu),在 Shadow DOM 外部的樣式代碼中,會(huì)是這樣的:

  • x-foo::shadow > span 可以匹配到 #top 元素
  • #top 匹配不到元素
  • x-foo /deep/ span 可以匹配到 #not-top 和 #top 元素

/deep/ 這個(gè)標(biāo)識(shí)的作用和我們的 > 選擇器有點(diǎn)類(lèi)似,只不過(guò)它是匹配其對(duì)應(yīng)的 Shadow DOM 內(nèi)部的,這個(gè)標(biāo)識(shí)可能還會(huì)變化,例如改成 >> 或者 >>> 之類(lèi)的,個(gè)人感覺(jué), >> 會(huì)更舒服。

最后一個(gè),用于在 Shadow DOM 內(nèi)部調(diào)整 slot 的樣式,在我查閱的這個(gè)文檔中,暫時(shí)是以 chrome 實(shí)現(xiàn)的為準(zhǔn),使用 ::content 偽類(lèi),不排除有更新為 ::slot 的可能性。我們看一個(gè)例子來(lái)了解一下,就算名稱(chēng)調(diào)整了也是差不多的用法:

  1. <x-foo> 
  2.   <div id="one" class="foo">...</div> 
  3.   <div id="two">...</div> 
  4.   <div id="three" class="foo"
  5.     <div id="four">...</div> 
  6.   </div> 
  7.   <"shadow tree"
  8.     <div id="five">...</div> 
  9.     <div id="six">...</div> 
  10.     <content select=".foo"></content> 
  11.   </"shadow tree"
  12. </x-foo>  

在 Shadow DOM 內(nèi)部的樣式代碼中,::content div 可以匹配到 #one,#three 和 #four,留意一下 #two 為什么沒(méi)被匹配到,因?yàn)樗鼪](méi)有被 content 元素選中,即不會(huì)進(jìn)行引用。如果更換成 slot 的 name 引用的方式亦是同理。

層疊規(guī)則,按照這個(gè)文檔的說(shuō)法,對(duì)于兩個(gè)優(yōu)先級(jí)別一樣的 CSS 聲明,沒(méi)有帶 !important 的,在 Shadow DOM 外部聲明的優(yōu)先級(jí)高于在 Shadow DOM 內(nèi)部的,而帶有 !important 的,則相反。個(gè)人認(rèn)為,這是提供給外部一定的控制能力,同時(shí)讓內(nèi)部可以限制一定的影響范圍。

繼承方面相對(duì)簡(jiǎn)單,在 Shadow DOM 內(nèi)部的頂級(jí)元素樣式從宿主元素繼承而來(lái)。

至此,Web Components 四個(gè)部分介紹結(jié)束了,其中有一些細(xì)節(jié),瀏覽器實(shí)現(xiàn)細(xì)節(jié),還有使用上的部分細(xì)節(jié),是沒(méi)有提及的,因?yàn)樵敿?xì)記錄的話,還會(huì)有很多東西,內(nèi)容很多。當(dāng)使用過(guò)程中有疑問(wèn)時(shí)可以再次查閱標(biāo)準(zhǔn)文檔,有機(jī)會(huì)的話會(huì)再完善這個(gè)文章。下一部分會(huì)把這四個(gè)內(nèi)容組合起來(lái),整體看下 Web Components 是怎么使用的。

Web Components

Web Components 總的來(lái)說(shuō)是提供一整套完善的封裝機(jī)制來(lái)把 Web 組件化這個(gè)東西標(biāo)準(zhǔn)化,每個(gè)框架實(shí)現(xiàn)的組件都統(tǒng)一標(biāo)準(zhǔn)地進(jìn)行輸入輸出,這樣可以更好推動(dòng)組件的復(fù)用。結(jié)合上邊各個(gè)部分的內(nèi)容,我們整合一起來(lái)看下應(yīng)該怎么使用這個(gè)標(biāo)準(zhǔn)來(lái)實(shí)現(xiàn)我們的組件:

  1. <!-- components/header.html --> 
  2. <template id=""
  3. <style> 
  4. ::content li { 
  5.   display: inline-block; 
  6.   padding: 20px 10px; 
  7. </style> 
  8. <content select="ul"></content> 
  9. </template> 
  10. <script> 
  11. (function() { 
  12.   const element = Object.create(HTMLInputElement.prototype) 
  13.   const template = document.currentScript.ownerDocument.querySelector('template'
  14.  
  15.   element.createdCallback = function() { 
  16.     const shadowRoot = this.createShadowRoot() 
  17.     const clone = document.importNode(template.content, true
  18.     shadowRoot.appendChild(clone) 
  19.  
  20.     this.addEventListener('click'function(event) { 
  21.       console.log(event.target.textContent) 
  22.     }) 
  23.   } 
  24.  
  25.   document.registerElement('test-header', { prototype: element }) 
  26. })() 
  27. </script> 

 

這是一個(gè)簡(jiǎn)單的組件的例子,用于定義一個(gè) test-header,并且給傳遞進(jìn)來(lái)的子元素 li 添加了一些組件內(nèi)部的樣式,同時(shí)給組件綁定了一個(gè)點(diǎn)擊事件,來(lái)打印點(diǎn)擊目標(biāo)的文本內(nèi)容。

看下如何在一個(gè) HTML 文件中引入并且使用一個(gè)組件:

  1. <!-- index.html --> 
  2. <!DOCTYPE html> 
  3. <html> 
  4.   <head> 
  5.     <meta charset="utf-8"
  6.     <title></title> 
  7.  
  8.     <link rel="import" href="components/header.html"
  9.   </head> 
  10.   <body> 
  11.     <test-header> 
  12.       <ul> 
  13.         <li>Home</li> 
  14.         <li>About</li> 
  15.       </ul> 
  16.     </test-header> 
  17.   </body> 
  18. </html> 

 

一個(gè) import 的 <link> 把組件的 HTML 文件引用進(jìn)來(lái),這樣會(huì)執(zhí)行組件中的腳本,來(lái)注冊(cè)一個(gè) test-header 元素,這樣子我們便可以在主文檔中使用這個(gè)元素的標(biāo)簽。

上邊的例子是可以在 chrome 正常運(yùn)行的。

所以,根據(jù)上邊簡(jiǎn)單的例子可以看出,各個(gè)部分的內(nèi)容是有機(jī)結(jié)合在一起,Custom Elements 提供了自定義元素和標(biāo)簽的能力,template 提供組件模板,import 提供了在 HTML 中合理引入組件的方式,而 Shadow DOM 則處理組件間代碼隔離的問(wèn)題。

不得不承認(rèn),Web Components 標(biāo)準(zhǔn)的提出解決了一些問(wèn)題,必須交由瀏覽器去處理的是 Shadow DOM,在沒(méi)有 Shadow DOM 的瀏覽器上實(shí)現(xiàn)代碼隔離的方式多多少少有缺陷。個(gè)人我覺(jué)得組件化的各個(gè) API 不夠簡(jiǎn)潔易用,依舊有 getElementById 這些的味道,但是交由各個(gè)類(lèi)庫(kù)去簡(jiǎn)化也可以接受,而 import 功能上沒(méi)問(wèn)題,但是加載多個(gè)組件時(shí)性能問(wèn)題還是值得商榷,標(biāo)準(zhǔn)可能需要在這個(gè)方面提供更多給瀏覽器的指引,例如是否有可能提供一種單一請(qǐng)求加載多個(gè)組件 HTML 的方式等。

在現(xiàn)在的移動(dòng)化趨勢(shì)中,Web Components 不僅僅是 Web 端的問(wèn)題,越來(lái)越多的開(kāi)發(fā)者期望以 Web 的方式去實(shí)現(xiàn)移動(dòng)應(yīng)用,而多端復(fù)用的實(shí)現(xiàn)漸漸是以組件的形式鋪開(kāi),例如 React Native 和 Weex。所以 Web Components 的標(biāo)準(zhǔn)可能會(huì)影響到多端開(kāi)發(fā) Web 化的一個(gè)模式和發(fā)展。

最后,再啰嗦一句,Web Components 個(gè)人覺(jué)得還是未來(lái)發(fā)展趨勢(shì),所以才有了這個(gè)文章。

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2015-04-08 10:40:09

2019-01-11 10:39:24

軟件架構(gòu)虛擬空間機(jī)器人

2020-04-07 08:05:51

程序員互聯(lián)網(wǎng)職業(yè)

2016-12-07 18:10:08

邊緣計(jì)算

2013-06-26 10:49:09

云端大腦科技技術(shù)

2022-06-13 23:30:27

代碼詞匯高質(zhì)量

2010-08-02 13:30:34

移動(dòng)開(kāi)發(fā)移動(dòng)開(kāi)發(fā)平臺(tái)

2024-02-26 09:13:35

WebComponents開(kāi)源項(xiàng)目

2009-08-02 22:32:44

綜合布線系統(tǒng)

2022-03-01 07:00:00

AI架構(gòu)師人工智能

2020-03-19 15:21:57

智慧城市藝術(shù)社會(huì)

2019-07-08 17:34:29

共享辦公ideaPod文印

2020-04-22 14:15:13

5G網(wǎng)絡(luò)技術(shù)

2017-04-06 15:00:38

編程語(yǔ)言

2019-05-13 15:45:29

程序員面試招聘

2020-09-23 08:55:16

交換機(jī)配置網(wǎng)絡(luò)vlan

2012-06-18 09:33:03

云計(jì)算IBM惠普

2022-10-30 15:03:25

人工智能倉(cāng)庫(kù)管理機(jī)器人

2009-10-26 13:36:10

BSM

2013-12-25 09:07:24

微軟鮑爾默諾基亞
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)