聊聊web-components的那些事儿~

129 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情

随着前端技术的不断发展,“模块化”的概念在前端领域愈发重要,es6提出的esm,以及更早的cjs,amd,cmd等等,都是围绕前端模块化提出的,并且现在流行的三大前端框架:vue、react、angular,都是基于模块化的方式进行开发

由此可见“模块化”的重要性,因此浏览器也提出了原生的模块化方案,即 web-components,它让我们可以自定义html标签并复用自定义内容,废话不多说,下面就详细聊聊它吧

ppx2.jpg

什么是web-components

web-components 是一组 Web 平台 API,建立在 Web 标准之上,它允许开发人员创建新的自定义可重用被封装的 HTML 标记在网页和 Web 应用程序中使用

web-components是一种技术体系规范,该体系下主要涉及了如下四种技术

  • HTML Imports
  • Custom Elements
  • Shadow DOM
  • HTML templates

下面来依次讲解它们

inte.jpg

HTML Imports

HTML imports 提供了一种在一个 HTML中包含和重用另一个 HTML 的方法,下面给一个例子

定义一个可重用的html

<!--test.html -->  
<template>  
  <div>hello world</div>  
</template>  
  
<script>  
  const dom = document.currentScript.ownerDocument.querySelector('template').content;  
  class Test extends HTMLElement {  
    constructor() {  
      super();  
      this.attachShadow({ mode'open' }).appendChild(dom);  
    }  
  }  
  
  // 注册组件  
  customElements.define('test'Test);  
</script>  
  
<style>  
  div {  
      font-weight:bold;
  }  
</style>

使用它

    <!DOCTYPE html>  
    <html lang="en">  
    <head>  
      <title>Web Components</title>  
      <!-- HTML Imports -->  
      <link rel="import" href="test.html">  
    </head>  
    <body>  
      <test></test>  
    </body>  
    </html>

需要注意的是由于 HTML Imports 已经废弃,我在最新的谷歌浏览器(版本 106.0.5249.119)上进行了测试,模板导入没有任何效果,所以不推荐使用此方式

Custom Elements

这种方式给了我们开发者自定义html标签的能力,通过将自定义的内容封装在我们的自定义标签内,可以很方便地实现模板内容的复用

它主要是通过 customElements.define(name, constructor, options) 来实现的,三个参数的含义如下

  • name:表示创建的元素名称,不能是单个单词,必须要有短横线
  • constructor:定义元素的类
  • options(可选):配置对象

需要说明的是,第二个参数里的类有两种类型

  • 继承 HTMLElement,使用时可以独立使用
class MyComp extends HTMLElement {  
    constructor() {  
      super();  
      let template = document.getElementById('template');  
      let templateContent = template.content;  
      const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));  
      }  
}  
customElements.define('my-comp', MyComp);

使用:<my-comp></my-comp>
  • 继承 HTMLParagraphElement,使用需要结合原生标签使用
class MyComp extends HTMLParagraphElement {  
  constructor() {  
    super();  
    let template = document.getElementById('template');  
    let templateContent = template.content;  
  
    const shadowRoot = this.attachShadow({mode: 'open'})  
      .appendChild(templateContent.cloneNode(true));  
  }  
}  
customElements.define('my-comp', MyComp, { extends: 'p' });

使用:<p is="my-comp"></p>

Shadow DOM

web-components的封装能力, Shadow DOM是最关键的一环,Shadow DOM 可以将 标记结构样式行为 隐藏起来,并与页面上的其他代码相隔离,可以看作是创建了一个沙箱环境,内部和外部都不会互相影响

其实很多原生标签就是浏览器里由shadow-dom来实现的,比如:videoaudio,可以通过浏览器的开发工具进行查看

HTML templates

template 是web-components提供的一个标签,它的特性就是包裹在 template 中的 HTML 片段 不会 在页面加载的时候解析渲染,但是可以被js访问到并进行操作,我们可以通过它来定义自定义标签的结构、样式与行为

由于 HTML Imports 特性被废弃,因此目前定义模板的方式是将其放入js文件中,具体使用方式如下

const template = document.createElement('template');

template.innerHTML = `
    <style>
      span{
        color:red
      }
    </style>
    <span id='text'>Hello WebComponent</span>
`
class MyComp extends HTMLElement {  
  constructor() {  
      super();  
      let templateContent = template.content.cloneNode(true);  
      const shadow = this.attachShadow({mode: 'open'})
      templateContent.querySelector("#text").addEventListener('click',()=>{
         alert('test')
      })
      shadow.appendChild(templateContent);
  }    
}  
customElements.define('my-comp', MyComp);