前言
Web Components涉及到的內容還是很多的,每一塊都有很多東西可以講,國外的好多大佬已經產出了好多優秀的文章。 本文照常只是簡單瞭解大致內容而不進入深究,瞭解且會用即可,淺嘗輒止。
簡介
Web Components本身不是一個規範,而是由W3C提出的另外4個規範的合集。這四個規範是:
下面我們走馬觀花,簡單瞭解一個這四個東西。
HTML Template
之前的頁面開發經常的一個做法是把模板放在一個script標籤或者隱藏的div中,用的時候通過innerHTML取出,塞進資料, 然後放回頁面顯示。現在我們可以通過<template>標籤存放了。就像這樣:
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
複製程式碼
特性檢測
要特性檢測 <template>,可以建立一個 template 元素並檢查它是否擁有 content 屬性:
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
// 檢測通過!
} else {
// 使用舊的模板技術或庫。
}
複製程式碼
啟用模板
啟用模板,即渲染出模板裡面的內容。啟用模板最簡單的方法就是使用 document.importNode() 對模板的 .content 進行深拷貝。 .content 為只讀屬性,關聯一個包含模板內容的 DocumentFragment。
var t = document.querySelector('#mytemplate');
// 在執行時填充 src。
t.content.querySelector('img').src = 'logo.png';
var clone = document.importNode(t.content, true);
document.body.appendChild(clone);
複製程式碼
特點
用 <template> 來包裹內容為我們提供了幾個重要屬性:
-
它的內容在啟用之前一直處於惰性狀態。本質上,這些標記就是隱藏的 DOM,它們不會被渲染。
-
處於模板中的內容不會有副作用。指令碼不會執行,圖片不會載入,音訊不會播放,...直到模板被使用。
-
內容不在文件中。在主頁面使用 document.getElementById() 或 querySelector() 不會返回模板的子節點。
-
模板能夠被放置在任何位置,包括 <head>,<body>,或 <frameset>,並且任何能夠出現在以上元素中的內容都可以放到模板中。 注意,"任何位置"意味著 <template> 能夠安全的出現在 HTML 解析器不允許出現的位置... 幾乎可以作為任何內容模型的子節點, 它也可以作為 <table> 或 <select> 的子元素。
推薦閱讀
HTML Imports
之前在頁面引入另一個頁面或片段往往是通過iframe或者ajax非同步載入,而現在我們可以這樣做:
在head中引入
<head>
<link rel="import" href="/path/to/imports/stuff.html">
</head>
複製程式碼
js中獲取
var content = document.querySelector('link[rel="import"]').import;
複製程式碼
特性檢測
要檢測瀏覽器是否支援匯入,可驗證 <link> 元素上是否存在 import:
function supportsImports() {
return 'import' in document.createElement('link');
}
if (supportsImports()) {
// 支援匯入
} else {
// 使用其他方法載入檔案
}
複製程式碼
推薦閱讀
Shadow DOM
首先需要設定一下:開啟開發者工具,f1開啟設定(或右上角三個點),然後勾上Show user agent shadow DOM ——
然後再看下,video標籤是這樣的 ——
甚至一個普通的input ——
之前被隱藏掉的DOM部分就是shadow DOM。顧名思義,它是其宿主元素的影子,通常用來封裝元件的內部結構。
所以像video、audio甚至input都是用簡單的元素封裝的元件。
這讓我想到,我們是不是可以通過修改元素裡面的shadow DOM的樣式來改變該元素的樣式呢? 答案是 —— 是的,但也不完全是...
從上圖audio標籤的結構和瀏覽器預設樣式可以看到,我們可以像這樣修改對應的樣式:
audio::-webkit-media-controls {
...
}
複製程式碼
就像通過::-webkit-scrollbar改造瀏覽的滾動條樣式那樣,
於是,預設的audio樣式(新版chrome)——
經過改造後,可以變成這樣——
然而並不是所有樣式都可以這樣覆蓋改造,像pseudo="-internal-media-controls-loading-panel"這樣以"-internal-"開頭的是不可以的。 所以這個做法還是有很大侷限性的。
這是我試出來的,並沒發現相關標準或依據...?
這種做法自認為只適合拿來玩玩而已,不適合投入到專案開發中去。一來是因為其侷限性太大,二來誰知道啥時候瀏覽器升級,這些標籤的內部結構就又變化了呢, 最重要的是shadow DOM是為web Components而生的,與Custom Elements一起是web Components的重要組成部分,並非用於此“旁門左道”?。
推薦閱讀
- Using shadow DOM
- Shadow DOM 101
- Shadow DOM 201 - CSS and Styling
- Shadow DOM 301 - Advanced Concepts & DOM APIs
Custom Elements
自定義元素,首先有個硬性規定,自定義元素的命名中必須要有中劃線“-”,否則便是未知元素了。
自定義元素分為兩種 ——
自特性主自定義元素(Autonomous custom elements)
不具備任何已有元素的,其樣式和行為完全自定義,如我們要定義一個這樣的元素:
<flag-icon country="cn"></flag-icon>
複製程式碼
通過給屬性country賦值來顯示對應的國旗。
js的基本結構是這樣的
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static get observedAttributes() { return ["country"]; }
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
//...
}
}
//全域性註冊該元素
customElements.define("flag-icon", FlagIcon);
複製程式碼
註冊後,也通過js建立該元素
const flagIcon = document.createElement("flag-icon");
flagIcon.country = "cn";
document.body.appendChild(flagIcon);
複製程式碼
自定義內建元素(Customized built-in elements)
繼承自已有元素,擁有已有元素的所有特性。
比如我們自定義一個按鈕,整合普通按鈕所有的特性,但是當點選的時候會有一個動效,就可以這麼做 ——
class PlasticButton extends HTMLButtonElement {
constructor() {
super();
this.addEventListener("click", () => {
// 動效邏輯
});
}
}
複製程式碼
不同的是,註冊時要加上一個引數
customElements.define("plastic-button", PlasticButton, { extends: "button" });
複製程式碼
使用時也稍有不同
<button is="plastic-button">點我!</button>
複製程式碼
通過js定義元素,則是這樣
const plasticButton = document.createElement("button", { is: "plastic-button" });
plasticButton.textContent = "點我!";
document.body.appendChild(flagIcon);
複製程式碼
生命週期
用過Vue、React等框架的同學對生命週期應該不陌生。同樣,自定義元素有4個生命週期:
connectedCallback
元素首次被插入文件DOM時觸發
disconnectedCallback
元素從文件DOM中刪除時觸發
adoptedCallback
元素被移動到新的文件時觸發
attributeChangedCallback
元素增加、刪除、修改自身屬性時觸發
推薦閱讀
來一個demo
評分元件相信大家都司空見慣了。照葫蘆畫瓢,我用原生js寫了一個Web Components 版的,簡單實現了該元件的基本功能。
demo截圖:
推薦閱讀
總結
Web Components 為前端元件化提供瞭解決方案,但用慣了Vue這樣的框架,還是會發現Web Components 的問題, 比如
- 瀏覽器的支援
- 對樣式區域性作用域的處理,往往js中包著一堆的CSS樣式,略顯臃腫
- 父子、兄弟元件的通訊問題
- 屬性都是字串,需要額外的程式碼做轉換和相容
- 沒有資料驅動,基本全是DOM操作
個人愚見,望大佬指點!?