国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript 是如何工作: Shadow DOM 的內部結構+如何編寫獨立的組件!

godlong_X / 1712人閱讀

摘要:向影子樹添加的任何內容都將成為宿主元素的本地元素,包括,這就是影子實現樣式作用域的方式。

這是專門探索 JavaScript 及其所構建的組件的系列文章的第 17 篇。

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你!

如果你錯過了前面的章節,可以在這里找到它們:

JavaScript 是如何工作的:引擎,運行時和調用堆棧的概述!

JavaScript 是如何工作的:深入V8引擎&編寫優化代碼的5個技巧!

JavaScript 是如何工作的:內存管理+如何處理4個常見的內存泄漏!

JavaScript 是如何工作的:事件循環和異步編程的崛起+ 5種使用 async/await 更好地編碼方式!

JavaScript 是如何工作的:深入探索 websocket 和HTTP/2與SSE +如何選擇正確的路徑!

JavaScript 是如何工作的:與 WebAssembly比較 及其使用場景!

JavaScript 是如何工作的:Web Workers的構建塊+ 5個使用他們的場景!

JavaScript 是如何工作的:Service Worker 的生命周期及使用場景!

JavaScript 是如何工作的:Web 推送通知的機制!

JavaScript是如何工作的:使用 MutationObserver 跟蹤 DOM 的變化!

JavaScript是如何工作的:渲染引擎和優化其性能的技巧!

JavaScript是如何工作的:深入網絡層 + 如何優化性能和安全!

JavaScript是如何工作的:CSS 和 JS 動畫底層原理及如何優化它們的性能!

JavaScript的如何工作的:解析、抽象語法樹(AST)+ 提升編譯速度5個技巧!

JavaScript是如何工作的:深入類和繼承內部原理+Babel和 TypeScript 之間轉換!

JavaScript是如何工作的:存儲引擎+如何選擇合適的存儲API!

概述

Web Components 是一套不同的技術,允許你創建可重用的定制元素,它們的功能封裝在你的代碼之外,你可以在 Web 應用中使用它們。

Web組件由四部分組成:

Shadow DOM(影子DOM)

HTML templates(HTML模板)

Custom elements(自定義元素)

HTML Imports(HTML導入)

在本文中主要講解 Shadow DOM(影子DOM)

Shadow DOM 這款工具旨在構建基于組件的應用。因此,可為網絡開發中的常見問題提供解決方案:

隔離 DOM:組件的 DOM 是獨立的(例如,document.querySelector() 不會返回組件 shadow DOM 中的節點)。

作用域 CSS:shadow DOM 內部定義的 CSS 在其作用域內。樣式規則不會泄漏,頁面樣式也不會滲入。

組合:為組件設計一個聲明性、基于標記的 API。

簡化 CSS - 作用域 DOM 意味著您可以使用簡單的 CSS 選擇器,更通用的 id/類名稱,而無需擔心命名沖突。

Shadow DOM

本文假設你已經熟悉 DOM 及其它的 Api 的概念。如果不熟悉,可以在這里閱讀關于它的詳細文章—— https://developer.mozilla.org...。

陰影 DOM 只是一個普通的 DOM,除了兩個區別:

創建/使用的方式

與頁面其他部分有關的行為方式

通常,你創建 DOM 節點并將其附加至其他元素作為子項。 借助于 shadow DOM,您可以創建作用域 DOM 樹,該 DOM 樹附加至該元素上,但與其自身真正的子項分離開來。這一作用域子樹稱為影子樹。被附著的元素稱為影子宿主。 您在影子中添加的任何項均將成為宿主元素的本地項,包括 Launch 模板 (Templates)

如果需要 Web 頁面上重復使用相同的標簽結構時,最好使用某種類型的模板,而不是一遍又一遍地重復相同的結構。這在以前也是可以實現,但是 HTML

現在自定義組件可以這樣使用:



元素

模板有一些缺點,主要是靜態內容,它不允許我們渲染變量/數據,好可以讓我們按照一般使用的標準 HTML 模板的習慣來編寫代碼。Slot 是組件內部的占位符,用戶可以使用自己的標記來填充。讓我們看看上面的模板怎么使用 slot


如果在標記中包含元素時沒有定義插槽的內容,或者瀏覽器不支持插槽, 就只展示文本 “Default text”

為了定義插槽的內容,應該在 元素中包含一個 HTML 結構,其中的 slot 屬性的值為我們定義插槽的名稱:


 Let"s have some different text!

可以插入插槽的元素稱為 Slotable; 當一個元素插入一個插槽時,它被稱為開槽 (slotted)。

注意,在上面的例子中,插入了一個 元素,它是一個開槽元素,它有一個屬性 slot,它等于 my-text,與模板中的 slot 定義中的 name 屬性的值相同。

在瀏覽器中渲染后,上面的代碼將構建以下扁平 DOM 樹:


  #shadow-root
  

Let"s have some different text!

設定樣式

使用 shadow DOM 的組件可通過主頁來設定樣式,定義其自己的樣式或提供鉤子(以 CSS 自定義屬性的形式)讓用戶替換默認值。

組件定義的樣式

作用域 CSS 是 Shadow DOM 最大的特性之一:

外部頁面的 CSS 選擇器不應用于組件內部

組件內定義的樣式不會影響頁面的其他元素,它們的作用域是宿主元素

shadow DOM 內部使用的 CSS 選擇器在本地應用于組件實際上,這意味著我們可以再次使用公共vid/類名,而不用擔心頁面上其他地方的沖突,最佳做法是在 Shadow DOM 內使用更簡單的 CSS 選擇器,它們在性能上也不錯。

看看在 #shadow-root 定義了一些樣式的:

#shadow-root


上面例子中的所有樣式都是#shadow-root的本地樣式。使用元素在#shadow-root中引入樣式表,這些樣式表也都屬于本地的。

:host 偽類選擇器

使用 :host 偽類選擇器,用來選擇組件宿主元素中的元素 (相對于組件模板內部的元素)。



當涉及到 :host 選擇器時,應該小心一件事:父頁面中的規則具有比元素中定義的 :host 規則具有更高的優先級,這允許用戶從外部覆蓋頂級樣式。而且 :host 只在影子根目錄下工作,所以你不能在Shadow DOM 之外使用它。

如果 :host() 的函數形式與 匹配,你可以指定宿主,對于你的組件而言,這是一個很好的方法,它可讓你基于宿主將對用戶互動或狀態的反應行為進行封裝,或對內部節點進行樣式設定:


:host-context()

:host-context() 或其任意父級與 匹配,它將與組件匹配。 例如,在文檔的元素上可能有一個用于表示樣式主題 (theme) 的 CSS 類,而我們應當基于它來決定組件的樣式。
比如,很多人都通過將類應用到 或 進行主題化:


  

在下面的例子中,只有當某個祖先元素有 CSS 類theme-light時,我們才會把background-color樣式應用到組件內部的所有元素中:

:host-context(.theme-light) h2 {
  background-color: #eef;
}
/deep/

組件樣式通常只會作用于組件自身的 HTML 上,我們可以使用 /deep/ 選擇器,來強制一個樣式對各級子組件的視圖也生效,它不但作用于組件的子視圖,也會作用于組件的內容。

在下面例子中,我們以所有的元素為目標,從宿主元素到當前元素再到 DOM 中的所有子元素:

:host /deep/ h3 {
  font-style: italic;
}

/deep/ 選擇器還有一個別名 >>>,可以任意交替使用它們。

/deep/>>> 選擇器只能被用在仿真 (emulated)模式下。 這種方式是默認值,也是用得最多的方式。
從外部為組件設定樣式

有幾種方法可從外部為組件設定樣式:最簡單的方法是使用標記名稱作為選擇器,如下

custom-container {
  color: red;
}

外部樣式比在 Shadow DOM 中定義的樣式具有更高的優先級。

例如,如果用戶編寫選擇器:

custom-container {
  width: 500px;
}

它將覆蓋組件的樣式:

:host {
  width: 300px;
}

對組件本身進行樣式化只能到此為止。但是如果人想要對組件的內部進行樣式化,會發生什么情況呢?為此,我們需要 CSS 自定義屬性。

使用 CSS 自定義屬性創建樣式鉤子

如果組件的開發者通過 CSS 自定義屬性提供樣式鉤子,則用戶可調整內部樣式。其思想類似于,但適用于樣式。

看看下面的例子:





在其 shadow DOM 內部:

:host([background]) {
  background: var(?-?custom-container-bg, #CECECE);
  border-radius: 10px;
  padding: 10px;
}

在本例中,該組件將使用 black 作為背景值,因為用戶指定了該值,否則,背景顏色將采用默認值 #CECECE

作為組件的作者,是有責任讓開發人員了解他們可以使用的 CSS 定制屬性,將其視為組件的公共接口的一部分。
在 JS 中使用 slot

Shadow DOM API 提供了使用 slot 和分布式節點的實用程序,這些實用程序在編寫自定義元素時遲早派得上用場。

slotchange 事件

slot 的分布式節點發生變化時,slotchange 事件將觸發。例如,如果用戶從 light DOM 中添加/刪除子元素。

var slot = this.shadowRoot.querySelector("#some_slot");
slot.addEventListener("slotchange", function(e) {
  console.log("Light DOM change");
});

要監視對 light DOM 的其他類型的更改,可以在元素的構造函數中使用 MutationObserver。以前討論過 MutationObserver 的內部結構以及如何使用它。

assignedNodes() 方法

有時候,了解哪些元素與 slot 相關聯非常有用。調用 slot.assignedNodes() 可查看 slot 正在渲染哪些元素。 {flatten: true} 選項將返回 slot 的備用內容(前提是沒有分布任何節點)。

讓我們看看下面的例子:

Default content

假設這是在一個名為 的組件中。

看看這個組件的不同用法,以及調用 assignedNodes() 的結果是什么:

在第一種情況下,我們將向 slot 中添加我們自己的內容:


   container text 

調用 assignedNodes() 會得到 [ container text ],注意,結果是一個節點數組。

在第二種情況下,將內容置空:

 

調用 assignedNodes() 的結果將返回一個空數組 []

在第三種情況下,調用 slot.assignedNodes({flatten: true}),得到結果是: [

默認內容

]

此外,要訪問 slot 中的元素,可以調用 assignedNodes() 來查看元素分配給哪個組件 slot

事件模型

值得注意的是,當發生在 Shadow DOM 中的事件冒泡時,會發生什么。

當事件從 Shadow DOM 中觸發時,其目標將會調整為維持 Shadow DOM 提供的封裝。也就是說,事件的目標重新進行了設定,因此這些事件看起來像是來自組件,而不是來自 Shadow DOM 中的內部元素。

下面是從 Shadow DOM 傳播出去的事件列表(有些沒有):

聚焦事件:blur、focus、focusin、focusout

鼠標事件:click、dblclick、mousedown、mouseenter、mousemove,等等

滾輪事件:wheel

輸入事件:beforeinput、input

鍵盤事件:keydown、keyup

組合事件:compositionstart、compositionupdate、compositionend

拖放事件:dragstart、drag、dragend、drop,等等

自定義事件

默認情況下,自定義事件不會傳播到 Shadow DOM 之外。如果希望分派自定義事件并使其傳播,則需要添加 bubbles: truecomposed: true 選項。

讓我們看看派發這樣的事件是什么樣的:

var container = this.shadowRoot.querySelector("#container");
container.dispatchEvent(new Event("containerchanged", {bubbles: true, composed: true}));
瀏覽器支持

如希望獲得 shadow DOM 檢測功能,請查看是否存在 attachShadow:

const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;

有史以來第一次,我們擁有了實施適當 CSS 作用域、DOM 作用域的 API 原語,并且有真正意義上的組合。 與自定義元素等其他網絡組件 API 組合后,shadow DOM 提供了一種編寫真正封裝組件的方法,無需花多大的功夫或使用如