其實這好像不是新聞,只是因為他還在 Working Draft 階段,目前的支援度也不高,所以資源相對少。然後,@Paul 寫了一個超強模組,詳見:Module 開發 - illuTrans
所以好像也該來筆記一下。
前言
Google 當初在 Polymer 就是主打 Web Components 這個東西的應用。只是因為太新,所以目前能應用的層面還不算多。況且好像 AngularJS 太紅,所以感覺 Polymer 就有點乏人問津的感覺。
這邊有 Google IO 在去年的演講,有興趣的人可以先看看,
回題,今天要聊一下 Web Components 到底是在做什麼事情。參考資訊來自 W3C 的 Web Components 介紹。
Web Components?
通常,我們製作一個網頁元件,就是把 HTML/CSS/JS 寫在你的 html 檔案裡面,而,Web Components 簡單來說,它能夠將你所製作的元件封裝起來,讓你的網頁元件可以被封裝成一包,然後利用自定義的標簽來使用你的元件。當然,由於目前瀏覽器支援程度尚未完全,所以,還是建議你搭配 Chrome 來看這些事情,不過,有些東西請必須打開,在 chrome://flags
當中,
- Experimental JavaScript
- Experimental Web Platform Features
- HTML Imports
這樣,你就能先行體驗一下 Web Components 所帶來的魔術。
Web Components 包含了這些項目,
- Templates, 就是你可以定義一個樣板備用
- Decorators, 裝飾樣式,其實主要的意思是,你可以使用 CSS 來表現你的樣板中的元素
- Custom Elements, 可以使用自定義元件,自定義標籤或是屬性的擴充等等
- Shadow DOM, 一種封裝過的 DOM,就是一種將界面元件封裝在自己的 DOM Tree 裡面的 DOM(好饒舌
- Imports, 可以從外部載入打包過的 Web Components(上述的都可以(除了 CSS
Templates
樣板顧名思義,就是把你的 Markup 用 <template>
包起來,舉例來說,
<template id="myTemplate">
<div class="slide">
<div class="slogan">
<h1>Slider</h1>
</div>
</div>
</template>
這個 <template>
有一個屬性 content
可用,當你要複製樣板內容時,可以使用這個 content
來進行操作,
var t = document.querySelector("#myTemplate");
var slide = t.content.cloneNode(true);
document.body.appendChild(slide);
Decorators
主要就是使用 CSS 來控制你的元素,他必須要包含一個 <template>
元件,所以正規的寫法會是像這樣,
<decorator id="myDecorator">
<template>
<div class="slide">
<content></content>
<div class="slogan">
<content select="slogan"></content>
</div>
</div>
</template>
</decorator>
你會發現其中有 <content>
標籤,這個標籤所代表的意義是,當你定義好相對應的 Decorators,它會將整個元件輸出成你所設定的樣子。而這個 <content>
就會取代你所設定的內容,舉個例子來說,
slider[open] {
decorator: url(#myDecorator);
}
當然你必須要準備一些內容,
<slider open>
<ul>
<li>First slide</li>
<li>Second slide</li>
<li>Third slide</li>
</ul>
<slogan>My Slider</slogan>
</slider>
不過很抱歉,因為這個東西實在太新,瀏覽器目前實作不出來(我照著 W3C 的內容改也沒用,假如你是用 W3C 所給的例子,
<details open>
<summary>Timepieces</summary>
<ul>
<li>Sundial
<li>Cuckoo clock
<li>Wristwatch
</ul>
</details>
你如果用 Chrome 開的話,瀏覽器自己會直接幫你實作出來,貌似跟 Web Components 無關(翻白眼!所以,這個部分我就不多著墨,等到官方有更多,或是瀏覽器真的能夠實作這個部分的時候,再來討論吧。
Custom Elements
跟 Decorators 頗類似,但是目前是 Web Components 的靈魂之一(另外一個是 Shadow DOM 待會會提。顧名思義就是,我可以自定義元件,然後在元件裡面做許多事情,例如 JS 或是 CSS 也都可以被寫在裡面。
基本定義
如何定義一個 <element>
呢?
<element name="my-button">
</element>
自定義元件可以使用擴充模式,加上屬性 extends
即可,
<element extends="button" name="my-button">
</element>
這個 extends
頗有意思,在定義上來說,我們所謂的自定義元素相對於 HTML 標籤語義來說,應該都是屬於語義無差別元素 (HTMLUnknownElement),而,當你使用 extends
的時候,就是告訴瀏覽器說,我這個自定義元素,他的語義是接近於某一個 HTML 元素的。
換句話說,上述的 my-button
這個自定義元素,我使用 extends
告訴你,我的這個元素與 <button>
是相似的。這個 extends
的用途大概就是這樣。如果你不使用 extends
的話,那麼瀏覽器就會直接以 name
賦予值來定義你這個元素當作是某一種語義,同樣的,也是屬於語義無差別元素。
定義方法與屬性
是,你沒有看錯,這樣的自定義元素可以被用於定義一種方法或是屬性,很類似把元素拿來當做 API 的載體,舉例來說,
<element name="etag">
<script>
({
debit: function() {
return 'always';
}
})
</script>
</element>
這樣,我們自定義的 etag
就會有一個方法,叫做 debit
的方法。推測遠通電收應該是都返回 always
生命週期的 Callbacks
在整個自定義元素的生命週期中,有三種 Callbacks 可以使用,
readyCallback
insertedCallback
removedCallback
意思就是 Callbacks 上面的意思(揍飛
剛剛有說過,我們可以在自定義元素中使用方法跟屬性,同樣的,我們也可以在元素中定義這三種 Callbacks 要做什麼樣的事情。有鑒於大家都是強大的前端工程師,就不多做解釋了,反正就是 Javascript 而已嘛(誒
在 HTML 中使用 Custom Elements
有個條件是,你的自定義元素必須由該元素擴展而來。但是,不確定是不是必要條件(官方也沒講。
<!-- 定義 Custom Elements -->
<element extends="button" name="my-button">
</element>
<!-- 使用 Custom Elements -->
<button is="my-button">
</button>
在 Script 中使用 Custom Elements
只要利用 document.register
就可以把自定義元素綁在你的 Javascript 上面。我想大家的 Javascript 都比我強,所以就不多作解釋。
var myButton = document.register('button', 'my-button', {
prototype: Object.create(HTMLButtonElement.prototype, {})
});
var b = new myButton();
自定義元素的定義也可以透過 document.createElement
來做到,以下直接給上 W3C 飯粒範例(不解釋
var b = document.createElement('button', 'fancy-button');
alert(b.outerHTML); // will display '<button is="fancy-button"></button>'
var c = document.createElement('tick-tock-clock');
alert(c.outerHTML); // will display '<tick-tock-clock></tick-tock-clock>'
更新元素
可以透過 CSS 的擬似類別 :unresolved
來更新自定義元素的內容。或許你會問這三尛?嗯,這是為了 Web Components 所特別衍生出來的一種擬似類別,在 CSS 正規文件上目前是看不到的。類似的東西還有前陣子剛出現的 Cat & Hat(不解釋
<style>
tick-tock-clock:unresolved {
content: '??:??';
}
</style>
<tick-tock-clock></tick-tock-clock> <!-- will show ??:?? -->
擴展自定義元素
就是剛剛的 extends
只是我們現在拿來擴展自定義元素,這麼做有好處嗎?有啊,請自行參透。
Shadow DOM
另外一個重頭戲,這個概念應該是從 Chrome 來的,然後就這樣紅了起來。或許是 Google 的另外一種邪惡事跡?他的概念就是,在一個元素底下,包含了一整個 #document
完整的 DOM Tree,而這個 DOM 就把他稱作 Shadow DOM。
這個 Shadow DOM 搭配 Template, Custom Elements 可以做相當多邪惡事情,不過,因為這種操作在 DevTools 並不容易觀察出來,所以在開發上會有一點難度(或是挫折...
Shadow DOM 有許多種模式,不過這如果要講起來可能又是好幾篇的事情,所以這裡就先不解釋這麼多了。況且目前的文件,也還是屬於 Working Draft 的階段,有興趣的人可以自己去看看。
單一插入點
其實,你可以利用 Chrome 的 DevTools 去看一些 <input>
元素,他的狀態就類似這樣。如果以自定義元素來說,就是擁有一個或多個 content
這樣的元件,Shadow DOM 就是從這樣的元素來插入。
重新繪製
我比較喜歡原文直接翻譯,重投影 (Reprojection)單純是因為我很愛投影這兩個字,意思就是呢,我可以利用樣板來重新產生我的原件內容,舉個例子來說,
請仔細看圖,你會發現左邊,跟右邊的 DevTools,所展示出的 .breaking
的位置不太一樣。這就是重新繪製元素的作用,它可以利用現有的樣板,將你原有存在於 HTML 的內容重新編排過。
備用內容
原文 Fallback Content,意思是,當你的插入點沒有任何資料的時候,可以拿這個 content
來取代。並不是說他可以相容 IE 好嗎!
Multiple Shadow Subtrees
意思就是你可以一直 Shadow DOM 下去,如果你覺得你不會被自己搞死的時候。當你要使用多層的 Shadow DOM 的時候,有些事情是必須要注意的,
- Shadow DOM 的宿主 (host) 只會有一個
- 每一個插入點都有順序
- 從宿主衍生出來的 Shadow DOM Tree 其根 (root) 無法被刪除,或者說,你不應該嘗試去任意刪除根
- 增加與刪除都有其順序(所以剛剛說不能任意刪除
至於他的工作方式,大抵上可以這樣解釋,
- 找到元素第一個 Shadow DOM,並且找到插入點(insertion points,
<content>
- 然後再往下找子元素,看是否有
<shadow>
子元素 - 然後一直循環的去找子元素與
<shadow>
子元素,如果都沒有了,就回到 Shadow DOM Tree 最上層準備渲染
CSS and Shadow DOM
這不太像是一般的 CSS Selector,所以你要對於 Shadow DOM 製作 CSS 樣式的時候,需要用比較特殊的選擇器來套用。例如,
#news::x-ticker {
/* Your seyle here. */
}
然後,他也能使用 @host
與 :scope
這樣的設定來做一些比較特別的應用。主要還是依據整個 Shadow DOM 的結構來跑。另一個比較特殊的是,它可以使用 var-
前輟,來讓 CSS 可以當成變數使用。
Events in Shadow DOM
大多數的事件都可以使用在 Shadow DOM 裡面,不過要特別留意的是一些元素上會發生的事情,例如 mouseover
這種,撰寫的時候要特別小心,不然你可能會拿到元素移動到自己身上的這種弔詭的事件響應。
就是元素本身監聽 mouseover
,然後 Shadow DOM 也有元素監聽 mouseover
的時候。
Imports
<link rem="import" href="templates.html">
不解釋!
小結
其實 Polymer 還是不錯的!真的!