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

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

ppx2.jpg

什么是web-components

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

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

下面来依次讲解它们

inte.jpg

HTML Imports

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

定义一个可重用的html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--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>

使用它

1
2
3
4
5
6
7
8
9
10
11
<!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) 来实现的,三个参数的含义如下

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

1
2
3
4
5
6
7
8
9
10
11
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>
1
2
3
4
5
6
7
8
9
10
11
12
13
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文件中,具体使用方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);