組件是前端的發展方向,如今流行的 React angular2.0 Vue 都是組件框架。javascript
谷歌公司因爲掌握了 Chrome 瀏覽器,一直在推進瀏覽器的原生組件,即Web Components 。相比第三方框架,原生組件簡單直接,符合直覺,不用加載任何外部模塊,代碼量小。目前,它還在不斷髮展,但已經可用於生產環境。css
Web Components 標準很是重要的一個特性是,它使開發者可以將HTML頁面的功能封裝爲 custom elements(自定義標籤)。html
首先咱們須要知道,Web Components 包括了四個部分:
• Custom Elements
• HTML Imports
• HTML Templates
• Shadow DOM
這四部分有機地組合在一塊兒,纔是 Web Components。
能夠用自定義的標籤來引入組件是前端組件化的基礎,在頁面引用 HTML 文件和 HTML 模板是用於支撐編寫組件視圖和組件資源管理,而 Shadow DOM 則是隔離組件間代碼的衝突和影響。前端
概述
Custom Elements 顧名思義,是提供一種方式讓開發者能夠自定義 HTML 元素,包括特定的組成,樣式和行爲。支持 Web Components 標準的瀏覽器會提供一系列 API 給開發者用於建立自定義的元素,或者擴展示有元素。
https://developer.mozilla.org...java
概述
HTML Imports 是一種在 HTMLs 中引用以及複用其餘的 HTML 文檔的方式。
咱們最多見的引入一個 css 文件的方式是:
<link rel="stylesheet" href="/css/master.css">
Web Components 如今提供多了一個這個:
<link rel="import" href="/components/header.html">
須要服務器環境,能夠用nodejs搭一個node
https://segmentfault.com/a/11...web
概述
這個東西很簡單,用過 handlebars 的人都知道有這麼一個東西:segmentfault
其餘模板引擎也有相似的東西,那麼 HTML Templates 即是把這個東西官方標準化,提供了一個 template 標籤來存放之後須要可是暫時不渲染的 HTML 代碼。
之後能夠這麼寫了:瀏覽器
Shadow DOM 好像提出很久了,最本質的需求是須要一個隔離組件代碼做用域的東西,例如我組件代碼的 CSS 不能影響其餘組件之類的。服務器
ShadowDOM-ShadowRoot
ShadowDOM主要解決一個文檔中可能須要大量交互的多個DOM樹創建和維護各自功能邊界的問題
HTML支持的其餘一些好比視頻、音頻甚至一些表單的控件,這些控件有些是由很複雜的界面組成的,其實這些界面也是用HTML+CSS寫的
例如<video>
https://www.bilibili.com/vide...
f12後只能看到一個video的標籤,但實際它還有個隱藏的shadowDom
咱們把瀏覽器裏的settings-Elements-show user agent shadow Dom 勾選上之後就能看到shadowDom
CSS 相關
由於 Shadow DOM 很大程度上是爲了隔離樣式做用域而誕生的,主文檔中的樣式規則不對 Shadow DOM 裏的子文檔生效,子文檔中的樣式規則也不影響外部文檔。
自定義元素能夠給它指定全局樣式
可是,組件的樣式應該與代碼封裝在一塊兒,只對自定義元素生效,不影響外部的全局樣式。因此,能夠把樣式寫在<template>裏面。
一個小例子:把card封裝成<user-card>標籤
效果以下:
card.html
<!DOCTYPE html\> <html\> <head\> <meta charset\="utf-8" /> <meta name\="viewport" content\="width=device-width" /> <title\>web Components</title\> <link rel\="stylesheet" type\="text/css" href\="a.css" /> </head\> <body\> <user-card image\="https://s0.2mdn.net/simgad/320245132277053394?sqp=-oaymwEOCKwCEPoBIAFIZFABWAE&rs=AOga4qmxkPL\_xXMXJ0ZrdWUAw31Jff3bmw" name\="User Name" email\="yourmail@some-email.com" \> </user-card\> <user-card image\="component.PNG" name\="小明" email\="yourmail@some-email.com" \> <span slot\="my-text"\>slot!</span\> </user-card\> <template id\="userCardTemplate"\> <style\> :host { display: flex; align-items: center; width: 450px; height: 180px; background-color: #d4d4d4; border: 1px solid #d5d5d5; box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); border-radius: 3px; overflow: hidden; padding: 10px; box-sizing: border-box; font-family: "Poppins", sans-serif; margin-bottom: 10px; } .image { flex: 0 0 auto; width: 160px; height: 160px; vertical-align: middle; border-radius: 5px; } .container { box-sizing: border-box; padding: 20px; height: 160px; } .container > .name { font-size: 20px; font-weight: 600; line-height: 1; margin: 0; margin-bottom: 5px; } .container > .email { font-size: 12px; opacity: 0.75; line-height: 1; margin: 0; margin-bottom: 15px; } .container > .button { padding: 10px 25px; font-size: 12px; border-radius: 5px; text-transform: uppercase; } </style\> <img class\="image" /> <div class\="container"\> <p class\="name"\></p\> <p class\="email"\></p\> <button class\="button"\>Follow John</button\> </div\> <div\><slot name\="my-text"\></slot\></div\> </template\> </body\> <script type\="text/javascript" src\="./card.js"\></script\> </html\>
card.js
class UserCard extends HTMLElement { constructor() { super(); var shadow \= this.attachShadow({ mode: "open" }); var templateElem \= document.getElementById("userCardTemplate"); var content \= templateElem.content.cloneNode(true); content .querySelector("img") .setAttribute("src", this.getAttribute("image")); content.querySelector(".container>.name").innerText \= this.getAttribute( "name" ); content.querySelector(".container>.email").innerText \= this.getAttribute( "email" ); shadow.appendChild(content); console.log(this.shadowRoot); } } window.customElements.define("user-card", UserCard);
參考文章:http://www.ruanyifeng.com/blo...
參考文章:
https://juejin.im/post/57c40f...