原文地址css
Web Components涉及到的內容仍是不少的,每一塊都有不少東西能夠講,國外的好多大佬已經產出了好多優秀的文章。 本文照常只是簡單瞭解大體內容而不進入深究,瞭解且會用便可,淺嘗輒止。html
Web Components自己不是一個規範,而是由W3C提出的另外4個規範的合集。這四個規範是:前端
下面咱們蜻蜓點水,簡單瞭解一個這四個東西。vue
以前的頁面開發常常的一個作法是把模板放在一個script標籤或者隱藏的div中,用的時候經過innerHTML取出,塞進數據, 而後放回頁面顯示。如今咱們能夠經過<template>標籤存放了。就像這樣:html5
<template id="mytemplate">
<img src="" alt="great image">
<div class="comment"></div>
</template>
複製代碼
要特性檢測 <template>,能夠建立一個 template 元素並檢查它是否擁有 content 屬性:git
function supportsTemplate() {
return 'content' in document.createElement('template');
}
if (supportsTemplate()) {
// 檢測經過!
} else {
// 使用舊的模板技術或庫。
}
複製代碼
激活模板,即渲染出模板裏面的內容。激活模板最簡單的方法就是使用 document.importNode() 對模板的 .content 進行深拷貝。 .content 爲只讀屬性,關聯一個包含模板內容的 DocumentFragment。github
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> 來包裹內容爲咱們提供了幾個重要屬性:web
它的內容在激活以前一直處於惰性狀態。本質上,這些標記就是隱藏的 DOM,它們不會被渲染。ajax
處於模板中的內容不會有反作用。腳本不會運行,圖片不會加載,音頻不會播放,...直到模板被使用。chrome
內容不在文檔中。在主頁面使用 document.getElementById() 或 querySelector() 不會返回模板的子節點。
模板可以被放置在任何位置,包括 <head>,<body>,或 <frameset>,而且任何可以出如今以上元素中的內容均可以放到模板中。 注意,"任何位置"意味着 <template> 可以安全的出如今 HTML 解析器不容許出現的位置... 幾乎能夠做爲任何內容模型的子節點, 它也能夠做爲 <table> 或 <select> 的子元素。
以前在頁面引入另外一個頁面或片斷每每是經過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 {
// 使用其餘方法加載文件
}
複製代碼
首先須要設置一下:打開開發者工具,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的重要組成部分,並不是用於此「旁門左道」😆。
自定義元素,首先有個硬性規定,自定義元素的命名中必需要有中劃線「-」,不然即是未知元素了。
自定義元素分爲兩種 ——
不具有任何已有元素的,其樣式和行爲徹底自定義,如咱們要定義一個這樣的元素:
<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);
複製代碼
繼承自已有元素,擁有已有元素的全部特性。
好比咱們自定義一個按鈕,集成普通按鈕全部的特性,可是當點擊的時候會有一個動效,就能夠這麼作 ——
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個生命週期:
元素首次被插入文檔DOM時觸發
元素從文檔DOM中刪除時觸發
元素被移動到新的文檔時觸發
元素增長、刪除、修改自身屬性時觸發
評分組件相信你們都司空見慣了。照葫蘆畫瓢,我用原生js寫了一個Web Components 版的,簡單實現了該組件的基本功能。
demo截圖:
Web Components 爲前端組件化提供瞭解決方案,但用慣了Vue這樣的框架,仍是會發現Web Components 的問題, 好比
我的愚見,望大佬指點!🙏