web components 标准介绍

1,464 阅读5分钟

官方文档链接: 传送门

定义:Web Components 是一套技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。

作用: web components 用于做跨技术栈的可重用组件;从根本上彻底解决css样式的全局污染问题。

三要素:

  1. Custom elements(自定义元素):一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。
  2. Shadow DOM(影子DOM):一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  3. HTML templates(HTML模板): template 和 slot 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

基本使用步骤:

  1. 创建一个类或函数来指定web组件的功能。

    wordCount.html

    <body>
     <h1>Word count rating widget</h1>
    	<article contenteditable="">
    		Pellentesque ornare tellus sit amet massa tincidunt congue. Morbi cursus, tellus vitae pulvinar dictum, dui turpis faucibus ipsum, nec hendrerit augue nisi et enim. Curabitur felis metus, euismod et augue et, luctus dignissim metus. Mauris placerat tellus id efficitur ornare. Cras enim urna, vestibulum vel molestie vitae, mollis vitae eros. Sed lacinia scelerisque diam, a varius urna iaculis ut. Nam lacinia, velit consequat venenatis pellentesque, leo tortor porttitor est, sit amet accumsan ex lectus eget ipsum. Quisque luctus, ex ac fringilla tincidunt, risus mauris sagittis mauris, at iaculis mauris purus eget neque. Donec viverra in ex sed ullamcorper. In ac nisi vel enim accumsan feugiat et sed augue. Donec nisl metus, sollicitudin eu tempus a, scelerisque sed diam.
    <!--			<p is="word-count"></p>-->
    		<word-count>
    		</word-count>
    	</article>
    </body>
    

    wordCount.js

    class WordCount extends HTMLElement {
     constructor() {
     super();
     // 计数器指向父元素
     let parent = this.parentNode;
     function countWords(node) {
        let text = node.innerText || node.textContent;
        return text.trim().split('/\s+/g')[0].length;
     };
     let count = 'Words = ' + countWords(parent);
     // 创建shadowRoot节点
     const shadowRoot = this.attachShadow({mode: 'open'});
     // 创建文本节点 并且添加计数器
     let span = document.createElement('span');
     // 添加到shadowRoot上
     shadowRoot.appendChild(span);
    
     // 启动计时器 每200ms进行重新获取计数
     setInterval(() => {
       count = 'Words = ' + countWords(parent);
       span.innerText = count;
     }, 200)
     }
    }
    customElements.define('word-count',  WordCount); // 注册新的自定义元素
    
  2. 使用 CustomElementRegistry.define() 方法注册新自定义元素 。 接受三个参数 定义的元素名称、指定元素功能的类、可选的其所继承自的元素的options选项

  3. 关于shadow DOM 的使用。 使用Element.attachShadow() 方法将一个shadow DOM附加到自定义元素上。使用通常的DOM方法向shadow DOM中添加子元素、事件监听器等

下边的代码主要显示第三个参数的option选项在使用的时候的坑。

wordCount.html

<body>
  <h1>Word count rating widget</h1>
  	<article contenteditable="">
  		Pellentesque ornare tellus sit amet massa tincidunt congue. Morbi cursus, tellus vitae pulvinar dictum, dui turpis faucibus ipsum, nec hendrerit augue nisi et enim. Curabitur felis metus, euismod et augue et, luctus dignissim metus. Mauris placerat tellus id efficitur ornare. Cras enim urna, vestibulum vel molestie vitae, mollis vitae eros. Sed lacinia scelerisque diam, a varius urna iaculis ut. Nam lacinia, velit consequat venenatis pellentesque, leo tortor porttitor est, sit amet accumsan ex lectus eget ipsum. Quisque luctus, ex ac fringilla tincidunt, risus mauris sagittis mauris, at iaculis mauris purus eget neque. Donec viverra in ex sed ullamcorper. In ac nisi vel enim accumsan feugiat et sed augue. Donec nisl metus, sollicitudin eu tempus a, scelerisque sed diam.
  		<p is="word-count"></p>
  	</article>
</body>

class WordCount extends HTMLParagraphElement {
  constructor() {
    super();
    // 计数器指向父元素
    let parent = this.parentNode;
    function countWords(node) {
       let text = node.innerText || node.textContent;
       return text.trim().split('/\s+/g')[0].length;
    };
    let count = 'Words = ' + countWords(parent);
    // 创建shadowRoot节点
    const shadowRoot = this.attachShadow({mode: 'open'});
    // 创建文本节点 并且添加计数器
    let span = document.createElement('span');
    // 添加到shadowRoot上
    shadowRoot.appendChild(span);

    // 启动计时器 每200ms进行重新获取计数
    setInterval(() => {
      count = 'Words = ' + countWords(parent);
      span.innerText = count;
    }, 200)
  }
}
customElements.define('word-count',  WordCount, { extends: 'p' }); // 注册新的自定义元素 第三个参数为自定义元素继承自 p 标签

额外使用

  1. template 的使用

template元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以(原文为 may be)在运行时使用JavaScript实例化。

  1. slot 的使用 作为 Web Components 技术套件的一部分,是Web组件内的一个占位符。该占位符可以在后期使用自己的标记语言填充,这样您就可以创建单独的DOM树,并将它与其它的组件组合在一起。

完整示例(完整示例中显示了template 和 slot的使用方式 ) :

WordCount.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
	<style>
		#innerSpan {
			color: blue !important;
		}
		#innerp {
			color: blue !important;
		}
	</style>
</head>
<body>
    <h1>Word count rating widget</h1>
		<article contenteditable="" onclick="Test()">
			Pellentesque ornare tellus sit amet massa tincidunt congue. Morbi cursus, tellus vitae pulvinar dictum, dui turpis faucibus ipsum, nec hendrerit augue nisi et enim. Curabitur felis metus, euismod et augue et, luctus dignissim metus. Mauris placerat tellus id efficitur ornare. Cras enim urna, vestibulum vel molestie vitae, mollis vitae eros. Sed lacinia scelerisque diam, a varius urna iaculis ut. Nam lacinia, velit consequat venenatis pellentesque, leo tortor porttitor est, sit amet accumsan ex lectus eget ipsum. Quisque luctus, ex ac fringilla tincidunt, risus mauris sagittis mauris, at iaculis mauris purus eget neque. Donec viverra in ex sed ullamcorper. In ac nisi vel enim accumsan feugiat et sed augue. Donec nisl metus, sollicitudin eu tempus a, scelerisque sed diam.
<!--			<p is="word-count"></p>-->
			<word-count>
				<ul slot="element-name">
					<li>Let's have some different text!</li>
					<li>In a list!</li>
				</ul>
			</word-count>
		</article>

		<template id="testHtml">
			<style>
				div {
					color: white;
					width: 100%;
					height: 200px;
					background-color: #666;
					padding: 5px;
				}
			</style>
			<div>
				<slot name="element-name"></slot>
			</div>
		</template>

<script src="WordCount.js"></script>
<script>
  function Test() {
    console.log(232323)
  }
</script>
</body>
</html>

WordCount.js

class WordCount extends HTMLElement {
// class WordCount extends HTMLParagraphElement {
  constructor() {
    super();
    // 计数器指向父元素
    let parent = this.parentNode;
    function countWords(node) {
       let text = node.innerText || node.textContent;
       return text.trim().split('/\s+/g')[0].length;
    };
    let p = document.createElement('p');
    this.parentNode.appendChild(p)
    p.setAttribute('id', 'innerp');
    p.innerText = '测试';
    p.style.color = 'red';
    let count = 'Words = ' + countWords(parent);
    // 创建shadowRoot节点
    const shadowRoot = this.attachShadow({mode: 'open'});
    // 创建文本节点 并且添加计数器
    let span = document.createElement('span');
    let button = document.createElement('button');
    span.innerText = count;
    span.setAttribute('id', 'innerSpan');
    span.style.color = 'red';
    button.innerText = '点我';
    // 添加到shadowRoot上
    shadowRoot.appendChild(span);
    shadowRoot.appendChild(button);

    button.onclick = function() {
      console.log(`count is ${count}`)
    }
    // button.addEventListener('click', function () {
    //   console.log(`count is ${count}`)
    // },false)

    // 通过检查来测试浏览器是否支持HTML模板元素
// 用于保存模板元素的内容属性。
    if ('content' in document.createElement('template')) {

      // 使用现有的HTML tbody实例化表和该行与模板
      let t = document.getElementById('testHtml')

      // 克隆新行并将其插入表中
      shadowRoot.appendChild(t.content.cloneNode(true));

    } else {
      // 找到另一种方法来添加行到表,因为不支持HTML模板元素。
    }

    // 启动计时器 每200ms进行重新获取计数
    setInterval(() => {
      count = 'Words = ' + countWords(parent);
      span.innerText = count;
    }, 200)
  }
}

// customElements.define('word-count',  WordCount, { extends: 'p' });
customElements.define('word-count',  WordCount);