Web Components 上手指南

現在的前端開發基本離不開 React、Vue 這兩個框架的支撐,而這兩個框架下面又衍生出了許多的自定義組件庫:
  • Element(Vue)

  • Ant Design(React)

這些組件庫的出現,讓我們可以直接使用已經封裝好的組件,而且在開源社區的幫助下,出現了很多的模板項目( vue-element-admin、Ant Design Pro ),能讓我們快速的開始一個項目。

雖然 React、Vue 為我們的組件開發提供了便利,但是這兩者在組件的開發思路上,一個是自創的 JSX 語法,一個是特有的單文件模板的語法,兩者的目標都是想提供一種組件的封裝方法。畢竟都有其原創的東西在里面,和我們剛開始接觸的 Web 基礎的 HTML、CSS、JS 的方式還是有些出入的。今天介紹的就是,通過 HTML、CSS、JS 的方式來實現自定義的組件,也是目前瀏覽器原生提供的方案:Web Components。

什么是 Web Components?

Web Components 本身不是一個單獨的規范,而是由一組DOM API 和 HTML 規范所組成,用于創建可復用的自定義名字的 HTML 標簽,并且可以直接在你的 Web 應用中使用。

代碼的復用一直都是我們追求的目標,在 JS 中可復用的代碼我們可以封裝成一個函數,但是對于復雜的HTML(包括相關的樣式及交互邏輯),我們一直都沒有比較好的辦法來進行復用。要么借助后端的模板引擎,要么借助已有框架對 DOM API 的二次封裝,而 Web Components 的出現就是為了補足瀏覽器在這方面的能力。

如何使用 Web Components?

Web Components 中包含的幾個規范,都已在 W3C 和 HTML 標準中進行了規范化,主要由三部分組成:

  • Custom elements(自定義元素):一組 JavaScript API,用來創建自定義的 HTML標簽,并允許標簽創建或銷毀時進行一些操作;

  • Shadow DOM(影子DOM):一組 JavaScript API,用于將創建的 DOM Tree 插入到現有的元素中,且 DOM Tree 不能被外部修改,不用擔心元素被其他地方影響;

  • HTML templates(HTML模板):通過 <template><slot> 直接在 HTML 文件中編寫模板,然后通過 DOM API 獲取。

Custom elements(自定義元素)

瀏覽器提供了一個方法:customElements.define() , 來進行自定義標簽的定義。該方法接受三個參數:

  • 自定義元素的名稱,一個 DOMString 標準的字符串,為了防止自定義元素的沖突,必須是一個帶短橫線連接的名稱(e.g. custom-tag)。

  • 定義自定義元素的一些行為,類似于 React、Vue 中的生命周期。

  • 擴展參數(可選),該參數類型為一個對象,且需要包含 extends 屬性,用于指定創建的元素繼承自哪一個內置元素(e.g. { extends: 'p' })。

下面通過一些例子,演示其用法,完整代碼放到了 JS Bin 上。

創建一個新的 HTML 標簽

先看看如何創建一個全新的自定義元素。

class?HelloUser?extends?HTMLElement?{constructor()?{//?必須調用?super?方法super();//?創建一個?div?標簽const?$box?=?document.createElement("p");let?userName?=?"User?Name";if?(this.hasAttribute("name"))?{//?如果存在?name?屬性,讀取?name?屬性的值userName?=?this.getAttribute("name");}//?設置?div?標簽的文本內容$box.innerText?=?`Hello?${userName}`;//?創建一個?shadow?節點,創建的其他元素應附著在該節點上const?shadow?=?this.attachShadow({?mode:?"open"?});shadow.appendChild($box);}
}//?定義一個名為?<hello-user?/>?的元素
customElements.define("hello-user",?HelloUser);
<hello-user?name="Shenfq"></hello-user>

這時候頁面上就會生成一個 <p> 標簽,其文本內容為:Hello Shenfq。這種形式的自定義元素被稱為:Autonomous custom elements,是一個獨立的元素,可以在 HTML 中直接使用。

擴展已有的 HTML 標簽

我們除了可以定義一個全新的 HTML 標簽,還可以對已有的 HTML 標簽進行擴展,例如,我們需要封裝一個與 <ul> 標簽能力類似的組件,就可以使用如下方式:

class?SkillList?extends?HTMLUListElement?{constructor()?{//?必須調用?super?方法super();if?(this.hasAttribute("skills")?&&this.getAttribute("skills").includes(','))?{//?讀取?skills?屬性的值const?skills?=?this.getAttribute("skills").split(',');skills.forEach(skill?=>?{const?item?=?document.createElement("li");item.innerText?=?skill;this.appendChild(item);})}}
}//?對?<ul>?標簽進行擴展
customElements.define("skill-list",?SkillList,?{?extends:?"ul"?});
<ul?is="skill-list"?skills="js,css,html"></ul>

對已有的標簽進行擴展,需要用到 customElements.define 方法的第三個參數,且第二參數的類,也需要繼承需要擴展標簽的對應的類。使用的時候,只需要在標簽加上 is 屬性,屬性值為第一個參數定義的名稱。

生命周期

自定義元素的生命周期比較簡單,一共只提供了四個回調方法:

  • connectedCallback:當自定義元素被插入到頁面的 DOM 文檔時調用。

  • disconnectedCallback:當自定義元素從 DOM 文檔中被刪除時調用。

  • adoptedCallback:當自定義元素被移動時調用。

  • attributeChangedCallback: 當自定義元素增加、刪除、修改自身屬性時調用。

下面演示一下使用方法:

class?HelloUser?extends?HTMLElement?{constructor()?{//?必須調用?super?方法super();//?創建一個?div?標簽const?$box?=?document.createElement("p");let?userName?=?"User?Name";if?(this.hasAttribute("name"))?{//?如果存在?name?屬性,讀取?name?屬性的值userName?=?this.getAttribute("name");}//?設置?div?標簽的文本內容$box.innerText?=?`Hello?${userName}`;//?創建一個?shadow?節點,創建的其他元素應附著在該節點上const?shadow?=?this.attachShadow({?mode:?"open"?});shadow.appendChild($box);}connectedCallback()?{console.log('創建元素')//?5s?后移動元素到?iframesetTimeout(()?=>?{const?iframe?=?document.getElementsByTagName("iframe")[0]iframe.contentWindow.document.adoptNode(this)},?5e3)}disconnectedCallback()?{console.log('刪除元素')}adoptedCallback()?{console.log('移動元素')}
}
<!--?頁面插入一個?iframe,將自定義元素移入其中?-->
<iframe?width="0"?height="0"></iframe>
<hello-user?name="Shenfq"></hello-user>

在元素被創建后,等待 5s,然后將自定義元素移動到 iframe 文檔中,這時候能看到控制臺會同時出現 刪除元素移動元素 的 log。

Console

Shadow DOM(影子DOM)

在前面介紹自定義元素的時候,已經用到了 Shadow DOM。Shadow DOM 的作用是讓內部的元素與外部隔離,讓自定義元素的結構、樣式、行為不受到外部的影響。

我們可以看到前面定義的 <hello-user> 標簽,在控制臺的 Elements 內,會顯示一個 shadow-root ,表明內部是一個 Shadow DOM。

Shadow DOM

其實 Web Components 沒有提出之前,瀏覽器內部就有使用 Shadow DOM 進行一些內部元素的封裝,例如 <video> 標簽。我們需要現在控制臺的配置中,打開 Show user agent ashdow DOM 開關。

設置

然后在控制臺的 Elements 內,就能看到 <video> 標簽內其實也有一個 shadow-root

video 標簽

創建 Shadow DOM

我們可以在任意一個節點內部創建一個 Shadow DOM,在獲取元素實例后,調用 Element.attachShadow() 方法,就能將一個新的 shadow-root 附加到該元素上。

該方法接受一個對象,且只有一個 mode 屬性,值為 openclosed,表示 Shadow DOM 內的節點是否能被外部獲取。

<div?id="root"></div>
<script>//?獲取頁面的const?$root?=?document.getElementById('root');const?$p?=?document.createElement('p');$p.innerText?=?'創建一個?shadow?節點';const?shadow?=?$root.attachShadow({mode:?'open'});shadow.appendChild($p);
</script>
Shadow DOM

mode 的差異

前面提到了 mode 值為 openclosed,主要差異就是是否可以使用 Element.shadowRoot 獲取到 shadow-root 進行一些操作。

<div?id="root"></div>
<script>//?獲取頁面的const?$root?=?document.getElementById('root');const?$p?=?document.createElement('p');$p.innerText?=?'創建一個?shadow?節點';const?shadow?=?$root.attachShadow({mode:?'open'});shadow.appendChild($p);console.log('is?open',?$div.shadowRoot);
</script>
open mode
<div?id="root"></div>
<script>//?獲取頁面的const?$root?=?document.getElementById('root');const?$p?=?document.createElement('p');$p.innerText?=?'創建一個?shadow?節點';const?shadow?=?$root.attachShadow({mode:?'closed'});shadow.appendChild($p);console.log('is?closed',?$div.shadowRoot);
</script>
closed mode

HTML templates(HTML模板)

前面的案例中,有個很明顯的缺陷,那就是操作 DOM 還是得使用 DOM API,相比起 Vue 得模板和 React 的 JSX 效率明顯更低,為了解決這個問題,在 HTML 規范中引入了 <tempate><slot> 標簽。

使用模板

模板簡單來說就是一個普通的 HTML 標簽,可以理解成一個 div,只是這個元素內的所以內容不會展示到界面上。

<template?id="helloUserTpl"><p?class="name">Name</p><a?target="blank"?class="blog">##</a>
</template>

在 JS 中,我們可以直接通過 DOM API 獲取到該模板的實例,獲取到實例后,一般不能直接對模板內的元素進行修改,要調用 tpl.content.cloneNode 進行一次拷貝,因為頁面上的模板并不是一次性的,可能其他的組件也要引用。

//?通過?ID?獲取標簽
const?tplElem?=?document.getElementById('helloUserTpl');
const?content?=?tplElem.content.cloneNode(true);

我們在獲取到拷貝的模板后,就能對模板進行一些操作,然后再插入到 Shadow DOM 中。

<hello-user?name="Shenfq"?blog="http://blog.shenfq.com"?/><script>class?HelloUser?extends?HTMLElement?{constructor()?{//?必須調用?super?方法super();//?通過?ID?獲取標簽const?tplElem?=?document.getElementById('helloUserTpl');const?content?=?tplElem.content.cloneNode(true);if?(this.hasAttribute('name'))?{const?$name?=?content.querySelector('.name');$name.innerText?=?this.getAttribute('name');}if?(this.hasAttribute('blog'))?{const?$blog?=?content.querySelector('.blog');$blog.innerText?=?this.getAttribute('blog');$blog.setAttribute('href',?this.getAttribute('blog'));}//?創建一個?shadow?節點,創建的其他元素應附著在該節點上const?shadow?=?this.attachShadow({?mode:?"closed"?});shadow.appendChild(content);}}//?定義一個名為?<hello-user?/>?的元素customElements.define("hello-user",?HelloUser);
</script>

添加樣式

<template> 標簽中可以直接插入 <style> 標簽在,模板內部定義樣式。

<template?id="helloUserTpl"><style>:host?{display:?flex;flex-direction:?column;width:?200px;padding:?20px;background-color:?#D4D4D4;border-radius:?3px;}.name?{font-size:?20px;font-weight:?600;line-height:?1;margin:?0;margin-bottom:?5px;}.email?{font-size:?12px;line-height:?1;margin:?0;margin-bottom:?15px;}</style><p?class="name">User?Name</p><a?target="blank"?class="blog">##</a>
</template>

其中 :host 偽類用來定義 shadow-root的樣式,也就是包裹這個模板的標簽的樣式。

占位元素

占位元素就是在模板中的某個位置先占據一個位置,然后在元素插入到界面上的時候,在指定這個位置應該顯示什么。

<template?id="helloUserTpl"><p?class="name">User?Name</p><a?target="blank"?class="blog">##</a><!--占位符--><slot?name="desc"></slot>?
</template><hello-user?name="Shenfq"?blog="http://blog.shenfq.com"><p?slot="desc">歡迎關注公眾號:更了不起的前端</p>
</hello-user>

這里用的用法與 Vue 的 slot 用法一致,不做過多的介紹。

總結

到這里 Web Components 的基本用法就介紹得差不多了,相比于其他的支持組件化方案的框架,使用 Web Components 有如下的優點:

  • 瀏覽器原生支持,不需要引入額外的第三方庫;

  • 真正的內部私有化的 CSS,不會產生樣式的沖突;

  • 無需經過編譯操作,即可實現的組件化方案,且與外部 DOM 隔離;

Web Components 的主要缺點就是標準可能還不太穩定,例如文章中沒有提到的模板的模塊化方案,就已經被廢除,現在還沒有正式的方案引入模板文件。而且原生的 API 雖然能用,但是就是不好用,要不然也不會出現 jQuery 這樣的庫來操作 DOM。好在現在也有很多基于 Web Components 實現的框架,后面還會開篇文章專門講一講使用 Web Components 的框架 lit-htmllit-element

好啦,今天的文章就到這里了,希望大家能有所收獲。


最近組建了一個江西人的前端交流群,如果你也是江西人可以加我微信ruochuan12 拉你進群。


常駐推薦閱讀

若川知乎高贊:有哪些必看的 JS庫?
我在阿里招前端,我該怎么幫你?(現在還可以加模擬面試群)
如何拿下阿里巴巴 P6 的前端 Offer
如何準備阿里P6/P7前端面試--項目經歷準備篇
大廠面試官常問的亮點,該如何做出?
如何從初級到專家(P4-P7)打破成長瓶頸和有效突破
若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?

常駐末尾

你好,我是若川,江西人~(點擊藍字了解我)歷時一年只寫了一個學習源碼整體架構系列?有哪些必看的JS庫:jQuery、underscore、lodash、sentry、vuex、axios、koa、redux

  1. 關注若川視野,回復"pdf" 領取優質前端書籍pdf,回復"1",可加群長期交流學習

  2. 我的博客地址:https://lxchuan12.gitee.io?歡迎收藏

  3. 覺得文章不錯,可以?分享、點贊、在看?呀^_^另外歡迎留言交流~

小提醒:若川視野公眾號面試、源碼等文章合集在菜單欄中間【源碼精選】按鈕,歡迎點擊閱讀,也可以星標我的公眾號,便于查找

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276130.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276130.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276130.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

隱藏網頁文件的后綴(IIS測試通過)!

網上很多網站會看到如這樣的地址: /content?actadd&id1 /column?actedit&id2 原本是 /content.asp?actadd&id1 /column.asp?actedit&id2 這樣的效果就是在iis上做了下手腳&#xff0c;使用了rewrite重寫組件&#xff0c;就可以實現。 rewrite.rar轉載于:ht…

Linux下查看文件內容的ASCII碼以檢查內容的編碼一致

轉載鏈接&#xff1a;http://blog.csdn.net/tiantang46800/article/details/6460567 ascii查詢方式&#xff0c;查看文件以ascii顯示&#xff0c;od命令 隨著計算機飛速的發展&#xff0c;很多人開始學習Linux&#xff0c;怎樣才能學好Linux&#xff0c;一定要學好Linux的命令…

乘基取整法是什么_十進制小數轉二進制小數乘2取整法的直觀理解

乘2取整法介紹舉例&#xff1a;0.35轉換成二進制0.3520.7 取0(d1)0.721.4 取1(d2)0.420.8 取0(d3)0.821.6 取1(d4)0.621.2 取1(d5)0.220.4 取0(d6)直到滿足規定的位數為止所以(0.35)10(0.d1d2d3d4d5d6)2(0.010110)2這個方法不難掌握&#xff0c;就是有點不好理解&#xf…

如何遠程連接Windows和linux服務器

linux的方法在下面 Windows服務器遠程連接 登錄控制臺查看服務器系統是什么系統例如阿里云的ECS服務器 Windows系統可以使用微軟自帶的遠程工具進行連接&#xff0c;可以連接的系統有Windows server 和Windows 7-10 等等系列&#xff1b;Windows系統&#xff0c;例如Windows10系…

URL是什么

URL是什么意思&#xff1f; 懸賞分&#xff1a;0 - 提問時間2006-3-12 08:14我在玩QQ空間的時候&#xff0c;在添加音樂時會有一個添加URL的地方‘&#xff5e;我是想問那是什么意思&#xff1f;&#xff1f;&#xff1f;提問者&#xff1a; caoyiwang1107 - 魔法學徒 一級 其他…

手把手教你接入前端熱門抓包神器 - whistle

大家好&#xff0c;我是若川&#xff0c;今天推薦騰訊前端團隊的這篇好文。whistle 是一個基于 Node.js 的跨平臺網絡調試工具。最近隨著 Nohost 的開源&#xff0c;有不少同學問了 whistle 相關的問題&#xff0c;本篇文章將結合幾個常見的業務場景介紹如何在本地前端項目開發…

Linux命令之hexdump - ”十六“進制查看器

轉載鏈接&#xff1a;http://codingstandards.iteye.com/blog/805778 用途說明 hexdump命令一般用來查看”二進制“文件的十六進制編碼&#xff0c;但實際上它的用途不止如此&#xff0c;手冊頁上的說法是“ascii, decimal, hexadecimal, octal dump“&#xff0c;這也就是本文…

使用數據增強技術提升模型泛化能力

在《提高模型性能&#xff0c;你可以嘗試這幾招...》一文中&#xff0c;我們給出了幾種提高模型性能的方法&#xff0c;但這篇文章是在訓練數據集不變的前提下提出的優化方案。其實對于深度學習而言&#xff0c;數據量的多寡通常對模型性能的影響更大&#xff0c;所以擴充數據規…

關于不同用戶進入系統報錯的請求

我自己搞了個系統,用超級用戶進入系統正常,用普通用戶進入系統就報錯,Microsoft JET Database Engine (0x80040E07) 標準表達式中數據類型不匹配。 /xs/huiyuan/huiyuan_bf.asp, 第 203 行 代碼如下請各位高手幫忙 <% if request.Cookies("shiwei_username")"…

React 與 Vue 框架的設計思路大 PK

大家好&#xff0c;我是若川。今天分享一篇框架設計思路的好文。關于我 大家好我是花果山的大圣&#xff0c;今天很榮幸&#xff0c;有機會跟大家分享一下很多年輕人感興趣的話題《 Vue 和 React 設計思想 PK》,個人水平有限&#xff0c;如果有理解不到位的請傾盆&#xff0c;大…

php foreach id是否存在數組_請糾正這 5 個 PHP 編碼小陋習

在做過大量的代碼審查后&#xff0c;我經常看到一些重復的錯誤&#xff0c;以下是糾正這些錯誤的方法。在循環之前測試數組是否為空$items [];// ...if (count($items) > 0) {foreach ($items as $item) {// process on $item ...}}foreach以及數組函數 (array_*) 可以處理…

1161轉進制(C語言)

一&#xff1a;題目 二&#xff1a;思路分析 1.首先該題目讓我們使用遞歸求十進制轉其他進制 2.其次&#xff0c;我們要知道十進制轉換為其他進制怎么轉換&#xff0c;以例題所給的數據為例 由此圖可以看出&#xff0c;十進制轉換為其他進制&#xff0c;是輾轉相除法&#xf…

PHP異常處理

轉載鏈接&#xff1a;http://www.blogdaren.com/post-2030.html 版權聲明&#xff1a;除非注明&#xff0c;本文由( manon )原創&#xff0c;轉載請保留文章出處 本文鏈接&#xff1a;PHP register_shutdown_function函數詳解 腳本時常死掉,而且并不總是那么好看. 我們可不想…

應對無協議脫歐 葡萄牙機場將為英籍旅客設快速通道

中新網1月18日電 據臺灣《聯合報》援引英媒報道&#xff0c;英國首相特蕾莎?梅的脫歐協議遭下院否決后&#xff0c;英國無協議脫歐的可能性變大。葡萄牙總理科斯塔17日表示&#xff0c;里斯本當局正對機場開設特殊通道進行規劃&#xff0c;使英國旅客無論英國最后如何脫歐&…

javascript 日期控件

http://www.my97.net/dp/index.asp轉載于:https://www.cnblogs.com/Ken-Cai/archive/2010/04/08/1707080.html

6輪字節前端校招面試經驗分享

大家好&#xff0c;我是若川。最近金三銀四&#xff0c;今天分享一篇字節前端校招面試經驗的輕松好文&#xff0c;相信看完會有所收獲。也歡迎點擊下方卡片關注或者星標我的公眾號若川視野因為我錯過了2020年的秋招&#xff08;ps: 那時候連數據結構與算法都還沒學完&#xff0…

redis存opc_Redis集群的三種模式

一、主從模式通過持久化功能&#xff0c;Redis保證了即使在服務器重啟的情況下也不會損失(或少量損失)數據&#xff0c;因為持久化會把內存中數據保存到硬盤上&#xff0c;重啟會從硬盤上加載數據。但是由于數據是存儲在一臺服務器上的&#xff0c;如果這臺服務器出現硬盤故障等…

斥資近1億港元,小米二次回購

1月21日消息&#xff0c;小米集團發布公告稱&#xff0c;公司于1月18日回購了984.96萬股B類普通股股票&#xff0c;占已發行股份0.041%&#xff0c;平均價為每股B類股10.1527港元&#xff0c;總計斥資近1億港元。 這也是繼1月17日首次回購后&#xff0c;小米集團連續兩日出手進…

MySQL日期數據類型、時間類型使用總結

轉載鏈接&#xff1a;http://www.jb51.net/article/23966.htm MySQL 日期類型&#xff1a;日期格式、所占存儲空間、日期范圍 比較。 日期類型 存儲空間 日期格式 日期范圍 ------------ --------- --------------------- -------------------------------…

ios macos_設計師可以從iOS 14和macOS Big Sur中學到什么?

ios macos重點 (Top highlight)With the introduction of iOS 14 and macOS Big Sur, we are the witness of the next big thing in UI Design. Changes are not so revolutionary like in iOS 7 years before, but they undoubtedly present the trend UI Designers will fol…