简介

在咱们的日常工作中,'组件'一词很是相熟。无论是在react、vue我的项目中,都常常会去封装一些自定义个性化的组件,来达到某些特定的性能,然而这些组件都须要内部模块的反对。Web Components API便提供了一种形式,在不依赖内部模块的状况下封装自定义的组件。

Web Components API 介绍

它由三项次要技术组成:

  • Custom elements(自定义元素):一组JavaScript API,容许您定义custom elements及其行为,而后能够在您的用户界面中依照须要应用它们。
  • Shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM离开出现)并管制其关联的性能。通过这种形式,您能够放弃元素的性能公有,这样它们就能够被脚本化和款式化,而不必放心与文档的其余局部发生冲突。
  • HTML templates(HTML模板): template 和 slot 元素使您能够编写不在出现页面中显示的标记模板。而后它们能够作为自定义元素构造的根底被屡次重用。

Custom elements(自定义元素)

自定义元素 Custom elements 是 Web components 技术的外围。

CustomElementRegistry

CustomElementRegistry:蕴含自定义元素相干性能。

CustomElementRegistry.define()办法能够用来注册新的自定义元素。

window.customElements返回的就是CustomElementRegistry对象的援用。

留神点:

  • 自定义元素类的基类是 HTMLElement ,所以继承了 HTML 元素的个性。也能够继 HTMLElement 的子类,比方 HTMLDivElement 、 HTMLTableElement 、 HTMLButtonElement 等。
  • 自定义元素标签的名称必须蕴含连字符 - ,用来和内置的HTML标签做区别。
  • 浏览器如果没有解析到自定义元素,会当做空的 div 元素解决。
class MyComponent extends HTMLElement {  constructor() {    super();    // ...  }}// 注册一个自定义元素window.customElements.define('my-component', MyComponent);

创立自定义内置元素的扩大

is 属性:HTML的全局属性,应用 is 属性能够把一个HTML的内置属性标记成一个已注册的自定义内置元素。

class NewButton extends HTMLButtonElement {  // ...}// 注册时,提供元素的扩大window.customElements.define('new-button', NewButton, { extends: 'button' })// 应用时<button is="new-button">NewButton</button>

生命周期

自定义组件的非凡回调函数:

class MyComponent extends HTMLElement {  constructor() {    super()      }    connectedCallback(){    // 当自定义元素第一次被连贯到文档DOM时被调用  }  disconnectedCallback(){    // 当自定义元素与文档DOM断开连接时被调用  }  adoptedCallback(){    // 当自定义元素被挪动到新文档时被调用  }  attributeChangedCallback(){    // 当自定义元素的一个属性被减少、移除或更改时被调用  }}

自定义办法和属性

自定义元素就是javascript的类,因而自定义元素的办法和属性的用法和class一样。

class MyComponent extends HTMLElement {  constructor() {    super()      }  // 自定义办法  hello() {    const name = this.getAttribute('name')    console.log('hello' + name)  }    // 也能够设置取值器和赋值器  set name(name) {    const oldName = this.getAttribute('name')    if(name !== oldName) {      this.setAttribute('name', name)    }  }  get name() {    return this.getAttribute('name')  }}window.customElements.define('my-component', MyComponent);// 应用<my-component name="zhangsan"></my-component>const el = document.querySelector('my-component');el.hello();

css伪类

  • :defined :匹配任何已定义的元素,包含内置元素和自定义的元素。
  • :host :Shaow Host,组自定元素挂载的节点。

Shadow DOM(影子DOM)

如果咱们心愿自定义元素的外部代码不容许被内部拜访到,咱们能够设置Shadow DOM来将其与内部隔离,这样内部的设置无奈影响到其外部,而外部的设置也不会影响到内部。

Shadow DOM 特有的术语:

  • Shadow host:一个惯例 DOM节点,Shadow DOM 会被附加到这个节点上。
  • Shadow tree:Shadow DOM外部的DOM树。
  • Shadow boundary:Shadow DOM完结的中央,也是惯例 DOM开始的中央。
  • Shadow root: Shadow tree的根节点。

Shadow DOM 最大的益处:

  • 向用户暗藏细节,间接提供组件。
  • 能够封装外部样式表,不会影响到内部。

例子:

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width">  <title>Web Components</title>  <style>    .btn {      color: #000;      font-size: 30px;    }  </style></head><body><button class="btn">获取shadowRoot</button><my-component></my-component></body><script> class MyComponent extends HTMLElement {  constructor() {    super();    var con = document.createElement('div');    con.classList.add('btn');    con.innerHTML = "自定义元素";    let style = document.createElement('style');    style.innerText = '.btn { font-weight: 600; color: red;}';    this.appendChild(style);    this.appendChild(con);  }}window.customElements.define('my-component', MyComponent); </script></html>

不应用shadow Dom

在不应用shadow Dom的时,内外部款式相互影响,优先级受选择器和加载程序影响。

应用shadow Dom

应用shadow Dom的时,内外部款式互不影响。

class MyComponent extends HTMLElement {  constructor() {    super();    // 应用 shadow dom    var shadow = this.attachShadow( { mode: 'open' } );    var con = document.createElement('div');    con.classList.add('btn');    con.innerHTML = "自定义元素";    let style = document.createElement('style');    style.innerText = '.btn { font-weight: 600; color: red;}';    shadow.appendChild(style);    shadow.appendChild(con);  }}

mode设置

通过设置 mode 为 open 或 closed 可能管制是否能够在内部拜访到组件的shadowRoot。

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width">  <title>Web Components</title>  <style>    .btn {      color: #000;      font-size: 30px;    }  </style></head><body><button class="btn">获取shadowRoot</button><my-component></my-component></body><script> class MyComponent extends HTMLElement {  constructor() {    super();    var shadow = this.attachShadow( { mode: 'open' } );    var con = document.createElement('div');    con.classList.add('btn');    con.innerHTML = "自定义元素";    let style = document.createElement('style');    style.innerText = '.btn { font-weight: 600; color: red;}';    shadow.appendChild(style);    shadow.appendChild(con);  }}window.customElements.define('my-component', MyComponent); const btn = document.querySelector('.btn')btn.addEventListener('click',() => {  const el = document.querySelector('my-component');  // 获取 shadowRoot  console.log(el.shadowRoot)})</script></html>

当 mode:'open' 时,点击获取shadowRoot按钮:

当 mode:'closed' 时,点击获取shadowRoot按钮:

HTML templates(HTML模板)

template模版

上文中在JavaScript中撸dom、style是很麻烦的一件事。Web Components API提供了 template 标签,蕴含一个HTML片段,不会在文档初始化时渲染。然而能够在运行时应用JavaScript显示。

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width"></head><body><my-component  title="这是题目"  content="这是内容这是内容这是内容这是内容这是内容这是内容"></my-component>  <template id="template">  <style>   :host {     display: flex;     align-items: center;     width: 450px;     height: 180px;     background-color: #ccc;     border-radius: 8px;   }   .container {     margin: 20px;     height: 150px;   }   .container > .title {     font-size: 24px;     line-height: 30px;     margin-bottom: 15px;   }   .container > .content {     padding: 10px;     font-size: 14px;   }  </style>    <div class="container">    <p class="title"></p>    <p class="content"></p>  </div></template></body><script>class MyComponent extends HTMLElement {  constructor() {    super();    var shadow = this.attachShadow( { mode: 'closed' } );        var template = document.getElementById('template');    var content = template.content.cloneNode(true);    content.querySelector('.container>.title').innerText = this.getAttribute('title');    content.querySelector('.container>.content').innerText = this.getAttribute('content');    shadow.appendChild(content);  }}window.customElements.define('my-component', MyComponent);</script></html>

Slot

slot> 元素,也叫插槽。作为Web Components技术的一部分,是Web组件内的一个占位符。该占位符能够在前期应用本人的标记语言填充,这样您就能够创立独自的DOM树,并将它与其它的组件组合在一起。插槽也分为默认插槽和具名插槽。

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width"></head><body><my-component>  <div slot="left">left</div>  <div slot="right">right</div>  <div>content</div></my-component><template id="template">  <style>   :host {     display: flex;     align-items: center;     width: 480px;     height: 180px;     background-color: #ccc;     border-radius: 8px;   }   .container {     display: flex;   }   .container > .con {     height: 100px;     width: 100px;     flex-grow: 1;     font-size: 24px;   }  </style>    <div class="container">    <p class="con">      <slot name="left"></slot>    </p>    <p class="con">      <slot></slot>    </p>    <p class="con">      <slot name="right"></slot>    </p>  </div></template></body><script>class MyComponent extends HTMLElement {  constructor() {    super();    var shadow = this.attachShadow( { mode: 'closed' } );    var template = document.getElementById('template');    var content = template.content.cloneNode(true);    shadow.appendChild(content);  }}window.customElements.define('my-component', MyComponent);</script></html>

参考文献

MDN Web Components:
https://developer.mozilla.org...