Web Components是否是Web的將來

今天 ,Web 組件已經從本質上改變了HTML。初次接觸時,它看起來像一個全新的技術。Web組件最初的目的是使開發人員擁有擴展瀏覽器標籤的能力,能夠自由的進行定製組件。面對新的技術,你可能會以爲無從下手。那這篇文章將爲你揭開Web組件神祕的面紗。若是你已經熟知HTML標籤和DOM編程,已經擁有了大量可用的Web組件,那麼你已是Web組件專家了。javascript

Web 組件的現狀

隨着各式各樣的用戶需求,瀏覽器的原生組件已經沒法知足需求。Web組件也就變得愈來愈重要。css

咱們將以自定義一個傳統三方插件爲例來介紹Web組件。html

首先,須要引用插件的CSS和JavaScript資源:java

<link rel="stylesheet" type="text/css" href="my-widget.css" />

<script src="my-widget.js"></script>

接下來,咱們須要向頁面中添加佔位符。web

<div data-my-widget></div>

最後,咱們須要使用腳原本找到而且實例化這個佔位符爲Web組件。sql

// 使用 jQuery 初始化組件

$(function() {

$('[data-my-widget]').myWidget();

});

經過以上是三個基本步驟。已經完成了在頁面中添加了自定義插件,可是瀏覽器沒法肯定自定義組件的生命週期,若是經過如下方式聲明則使自定義組件生命週期變得清晰了。chrome

el.innerHTML = '<div data-my-widget></div>';

由於這不是一個內置的組件,咱們如今必須手動實例化新組件,編程

$(el).find('[data-my-widget]').myWidget();

避免這種複雜設置方法的有效方式是徹底抽象DOM交互。不過,這個動做也比較複雜,須要建立框架或者庫來自定義組件。瀏覽器

面臨的問題

組件一旦被聲明,佔位符已經被替代爲原生的HTML標記:ruby

<div data-my-widget>

<div class="my-widget-foobar">

<input type="text" class="my-widget-text" />

<button class="my-widget-button">Go</button>

</div>

</div>

這樣作的弊端是,自定義組件的標記和普通HTML組件的標記混雜在一塊兒,沒有清晰的分割和封裝。這就不可避免的會出現命名及樣式等衝突。

Web 組件的產生

隨着三方Web組件的發展,它已經成爲了Web開發不可或缺的部分:

<!—導入: -->

<link rel="import" href="my-widget.html" />

<!—使用:-->

<my-widget />

在這個實例中,咱們經過導入HTML來添加組件而且當即使用。

更重要的是,由於<my-widget />是瀏覽器原生支持的組件,它直接掛在瀏覽器的生命週期中,容許咱們像添加原生組件同樣添加三方組件。

el.innerHTML = '<my-widget />';

// 插件當前已經被實例化

當查看這個組件的HTML 源碼,你會發現它僅僅是一個單一的標籤。若是啓用瀏覽器Shadow DOM 特性,才能夠查看標籤內的組件,你將會發現一些有趣的事情,

clip_image001[1]

當咱們談論Web組件時,咱們不是在談論一門新技術。Web組件最初的目的是給咱們封裝能力,它能夠經過自定義組件和Shadow DOM 技術來實現。因此,接下來,咱們將着重介紹下這兩項技術。介紹以上兩個技術以前,咱們最好先梳理下已知瀏覽器原生組件。

已知的HTML組件

咱們知道組件能夠經過HTML標記或JavaScript來實例化:

使用 標記實例化:

<input type="text" />
document.createElement('input');
el.innerHTML = '<input type="text" />';

使用JaveScript實例化:

document.createElement('input') 

document.createElement('div')

添加帶有屬性的HTML標籤:

// 建立帶有屬性的input標籤...

el.innerHTML = '<input type="text" value="foobar" />';

//這時value屬性已經同步

el.querySelector('input').value;

組件能夠響應屬性的變化:

// 若是咱們更改value 屬性值
input.setAttribute('value', 'Foobar');

//屬性值會當即更改
input.value === 'Foobar'; // true

組件能夠有內部隱藏的DOM結構:

<!—使用一個input實現複雜的日曆功能-->
<input type="date" />
 
 // 儘管其內部結構比較複雜,可是已經封裝成爲一個組件
dateInput.children.length === 0; // true

組件可使用子組件:

<!—能夠給組件提供任意個 'option' 標籤-->

<select>

<option>1</option>

<option>2</option>

<option>3</option>

</select>

組件能夠爲其子組件提供樣式:

dialog::backdrop { background: rgba(0, 0, 0, 0.5); }

最後,組件能夠有內置樣式。和自定義插件不一樣,咱們不須要爲瀏覽器的原生控件引用CSS文件。

有了以上的瞭解,咱們已經具有了解Web組件的基礎。使用自定義組件和Shadow DOM,咱們能夠在咱們的插件中定義全部這些標準行爲。

自定義組件

註冊一個新組件也比較簡單:

var MyElement = document.register('my-element');

// 'document.register' 返回一個構造函器

你也許注意到上面的自定義組件名稱包含一個鏈接符。這是爲了確保自定義組件名稱不和瀏覽器內置組件不衝突。

如今<my-element />這個組件具有了原生組件的特性,

因此,自定義組件也一樣能夠進行普通的DOM操做:

document.create('my-element');

el.innerHTML = '<my-element />';

document.create('my-element');

構建自定義組件

當前,這個自定義組件僅僅有框架,而沒有內容,下面讓咱們向其中添加一些內容:

//咱們將提供'document.register'的第二個參數:

document.register('my-element', {

prototype: Object.create(HTMLElement.prototype, {

createdCallback: {

value: function() {

this.innerHTML = '<h1>ELEMENT CREATED!</h1>';

}

}

})

});

在這個例子中,咱們設置自定義組件的prototype,使用Object.create 方法建立一個繼承於HTMLElement的對象。在這個方法中修改該組件的屬性 innerHTML。

咱們定義了createdCallback方法,在每次聲明實例時調用。你一樣能夠有選擇性的定義attributeChangedCallback、 enteredViewCallback 和leftViewCallback等方法。

目前爲止咱們實現了動態修改自定義組件內容的功能,咱們仍然須要提供自定義組件的封裝方法,用於隱藏其內部組件。

使用Shadow DOM實現封裝

咱們須要完善下createdCallback方法。本次,除了修改innerHTML以外,咱們添加一些額外的操做:

createdCallback: {

value: function() {var shadow = this.createShadowRoot(); shadow.innerHTML = '<h1>SHADOW DOM!</h1>'; } }

在這個例子中, 你會注意到‘SHADOW DOM!’,可是查看源碼時你會發現只有空白的<my-element /> 標籤而已。這裏使用建立Shadow Root 方法替代了直接修改頁面。

Shadow Root中的任何組件,是肉眼可見的,可是和當前頁面的樣式和DOM API相隔離。這樣就實現了自定義組件是一個獨立組件的假象。

添加「輕量級DOM」

目前爲止,咱們的自定義組件是空標籤,可是若是向其中添加內部組件會出現什麼現象呢?

咱們假設自定義組件包含的節點以下,

<my-element>

這是一個輕量級 DOM。

<i>hello</i>

<i>world</i>

</my-element>

一旦針對於這個組件的 Shadow Root 被建立,它的子節點再也不存在。咱們這些隱藏的子節點封裝爲輕量級DOM節點。

若是禁用了 Shadow DOM,上面這個例子僅僅會顯示爲:這是一個輕量級 DOM‘ hello world ’。

當咱們在createdCallback方法中設置 Shadow DOM後,咱們可使用新增內容分配輕量級DOM組件到Shadow DOM 中。

createdCallback: {

value: function() {var shadow = this.createShadowRoot(); // 子組件'i' 標籤如今已經消失了  shadow.innerHTML = ‘輕量級 DOM 中的 "i" 標籤爲: ' + '<content select="i" />'; //如今,在 Shadow DOM 中只有 'i' 標籤是能夠見的。  } }

封裝樣式

Shadow DOM 最重要的做用是建立了和當前頁面隔離的Web組件,使Web組件不受當前頁面樣式和JaveScript腳本的影響。

createdCallback: {

value: function() {var shadow = this.createShadowRoot(); shadow.innerHTML = "<style>span { color: green }</style>" + "<span>I'm green</span>"; } }

反之,在 Shadow DOM 中定義的樣式也不會影響以外的標籤樣式。

<my-element />

<span>I'm not green</span>

揭露鉤子的祕密

當隱藏自定義組件內部標記,有時也須要在當前頁面對組件中的內部特定組件進行樣式設置。

例如,若是咱們自定義一個日曆插件,在不容許用戶控制整個插件的狀況下,容許最終用戶去定義按鈕的樣式。

這是其中的部分特性和僞組件:

createdCallback: {

value: function() {var shadow = this.createShadowRoot(); shadow.innerHTML = 'Hello <em part="world">World</em>'; } }

這是在當前頁面設置自定義組件內部組件樣式的方法:

my-element::part(world) { color: green; }

這部份內容介紹了封裝web組件的基本方式。Shadow DOM 是咱們能夠任意修改Web組件中的標籤。在例子中,咱們設置了「World」的樣式,可是使用者卻沒法判斷它是<em>標籤。

在你嘗試自定義Web組件以前,須要確保瀏覽器的相關特性已經打開。若是使用 Chrome,在 Chrome 中打開chrome://flags ,而且開啓「experimental Web Platform features」。

clip_image003[1]

這僅僅是個開始

全部本文中介紹的內容,都是模擬一些簡單的瀏覽器標準行爲。咱們已經習慣於和原生的瀏覽器組件進行交互,所以自定義組件的步驟並非想象中的那個難。Web組件最終提供咱們一種實現簡單、一致、可複用、封裝和組合部件的方法,這是一個有意義的開始。

相關文章
相關標籤/搜索