了解 Web Components

简介

Web Components 标准非常重要的一个特性是,它使开发者能够将HTML页面的功能封装为 custom elements(自定义标签)。

技术组成

1. Custome Elements:自定义元素,通过 JavaScript API 来创建。

两种类型

Autonomous custom elements 是独立的元素,它不继承其他内建的HTML元素。你可以直接把它们写成HTML标签的形式,来在页面上使用。例如 ,或者是document.createElement("popup-info")这样。

Customized built-in elements 继承自基本的HTML元素。在创建时,你必须指定所需扩展的元素(正如上面例子所示),使用时,需要先写出基本的元素标签,并通过 is 属性指定custom element的名称。例如

, 或者 document.createElement("p", { is: "word-count" })

MDN官方介绍

官方例子

main.js

// Create a class for the element
class Square extends HTMLElement {
  // Specify observed attributes so that
  // attributeChangedCallback will work
  static get observedAttributes() {
    return ['c', 'l'];
  }

  constructor() {
    // Always call super first in constructor
    super();

    const shadow = this.attachShadow({mode: 'open'});
    console.log('shaow-->', shadow);

    const div = document.createElement('div');
    const style = document.createElement('style');
    shadow.appendChild(style);
    shadow.appendChild(div);
  }

  connectedCallback() {
    console.log('Custom square element added to page.');
    updateStyle(this);
  }

  disconnectedCallback() {
    console.log('Custom square element removed from page.');
  }

  adoptedCallback() {
    console.log('Custom square element moved to new page.');
  }

  attributeChangedCallback(name, oldValue, newValue) {
    console.log('Custom square element attributes changed.');
    updateStyle(this);
  }
}

customElements.define('custom-square', Square);

function updateStyle(elem) {
  console.log('elem-->', elem);
  const shadow = elem.shadowRoot;
  shadow.querySelector('style').textContent = `
    div {
      width: ${elem.getAttribute('l')}px;
      height: ${elem.getAttribute('l')}px;
      background-color: ${elem.getAttribute('c')};
    }
  `;
}

const add = document.querySelector('.add');
const update = document.querySelector('.update');
const remove = document.querySelector('.remove');
let square;

update.disabled = true;
remove.disabled = true;

function random(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
}

add.onclick = function() {
  // Create a custom square element
  square = document.createElement('custom-square');
  square.setAttribute('l', '100');
  square.setAttribute('c', 'red');
  document.body.appendChild(square);

  update.disabled = false;
  remove.disabled = false;
  add.disabled = true;
};

update.onclick = function() {
  // Randomly update square's attributes
  square.setAttribute('l', random(50, 200));
  square.setAttribute('c', `rgb(${random(0, 255)}, ${random(0, 255)}, ${random(0, 255)})`);
};

remove.onclick = function() {
  // Remove the square
  document.body.removeChild(square);

  update.disabled = true;
  remove.disabled = true;
  add.disabled = false;
};

index.html



  
    
    Life cycle callbacks test
    
    
  
  
    

Life cycle callbacks test

运行结果

兼容性:Firefox、Chrome和Opera默认就支持 custom elements。Safari目前只支持 autonomous custom elements(自主自定义标签),而 Edge也正在积极实现中。

2. Shadow DOM:隔离 CSS 和 JavaScript,跟操作常规Dom类似。

元素就是使用shadow dom实现一系列的按钮和其他控制器。可以使用 Element.attachShadow({ mode: 'open' }) 方法来将一个 shadow root 附加到任何一个元素上。open表示可以通过页面内的 JavaScript 方法来获取 Shadow DOM{ mode: 'closed' }表示 Shadow DOM 是封闭的,不允许外部访问。

使用方式.

MDN官方介绍

image.png

相关术语

Shadow host:一个常规 DOM节点,Shadow DOM 会被附加到这个节点上。
Shadow tree:Shadow DOM内部的DOM树。
Shadow boundary:Shadow DOM结束的地方,也是常规 DOM开始的地方。
Shadow root: Shadow tree的根节点。

兼容性:Firefox(从版本 63 开始),Chrome,Opera 和 Safari 默认支持 Shadow DOM。

例子

main.js

// Create a class for the element
class PopUpInfo extends HTMLElement {
  constructor() {
    // Always call super first in constructor
    super();

    // Create a shadow root
    const shadow = this.attachShadow({mode: 'open'});

    // Create spans
    const wrapper = document.createElement('span');
    wrapper.setAttribute('class', 'wrapper');

    const icon = document.createElement('span');
    icon.setAttribute('class', 'icon');
    icon.setAttribute('tabindex', 0);

    const info = document.createElement('span');
    info.setAttribute('class', 'info');

    // Take attribute content and put it inside the info span
    const text = this.getAttribute('data-text');
    info.textContent = text;

    // Insert icon
    let imgUrl;
    if(this.hasAttribute('img')) {
      imgUrl = this.getAttribute('img');
    } else {
      imgUrl = 'img/default.png';
    }

    const img = document.createElement('img');
    img.src = imgUrl;
    icon.appendChild(img);

    // Apply external styles to the shadow dom
    const linkElem = document.createElement('link');
    linkElem.setAttribute('rel', 'stylesheet');
    linkElem.setAttribute('href', 'style.css');

    // Attach the created elements to the shadow dom
    shadow.appendChild(linkElem);
    shadow.appendChild(wrapper);
    wrapper.appendChild(icon);
    wrapper.appendChild(info);
  }
}

// Define the new element
customElements.define('popup-info', PopUpInfo);

style.css

.wrapper {
  position: relative;
}

.info {
  font-size: 0.8rem;
  width: 200px;
  display: inline-block;
  border: 1px solid black;
  padding: 10px;
  background: white;
  border-radius: 10px;
  opacity: 0;
  transition: 0.6s all;
  position: absolute;
  bottom: 20px;
  left: 10px;
  z-index: 3;
}

img {
  width: 1.2rem;
}

.icon:hover + .info, .icon:focus + .info {
  opacity: 1;
}

index.html

 

Pop-up info widget - web components

运行结果

shadow dom VS iframe

shadow dom

  1. 渲染得更快,占用内存更少;
  2. 具备较好的灵活性;
  3. 隔离样式;

iframe
MDN-iframe

  1. 增加内存和其他计算资源,因为每个浏览上下文都拥有完整的文档环境;
  2. 隔离样式
  3. 需要动态设置高度,否则内容超出高度,会显示滚动条;
  4. 无法操作内嵌表单
  5. 移动端兼容性差
  6. 需要服务器辅助配置,否则会出现跨域

dom嵌入页面提高性能的兼容性写法

let content = `
  

  

Element with Shadow DOM

`; let el = document.querySelector('.my-element'); if (document.body.attachShadow) { let shadow = el.attachShadow({ mode: 'open' }); // Allows JS access inside shadow.innerHTML = content; } else { let newiframe = document.createElement('iframe'); 'srcdoc' in newiframe ? newiframe.srcdoc = content : newiframe.src = 'data:text/html;charset=UTF-8,' + content; let parent = el.parentNode; parent.replaceChild(newiframe, el); }

3. Templates and Slots:用户在HTML中定义的模板,只有调用的时候才会被渲染。

可以通过使用