Web Component 是一种 W3C标准 支持的 组件化方案,经过它,咱们能够编写可复用的 组件,同时,咱们也能够对本身的组件作更精细化的控制。正如 PWA 同样,他并不是一项单一的技术,而是由三项技术组成:javascript
下面,咱们从一个简单的例子来入手。css
咱们准备编写一个 TextReverse
组件,TextReverse
只有一个很简单的功能,就是把传入的 字符串颠倒显示。html
例如: <text-reverse text='123'></text-reverse>
将会显示 321
。java
第一步,咱们须要 定义 这个自定义组件。浏览器
class TextReverse extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
const text = this.getAttribute('text') || '';
const wrapper = document.createElement('span');
wrapper.textContent = text.split('').reverse().join('');
shadowRoot.appendChild(wrapper);
}
}
复制代码
定义组件的方式也十分简单,咱们只须要 继承一下 HTMLElement
,而后在 构造函数 中编写本身的 初始化逻辑 就能够了。app
初始化过程当中,咱们首先 建立了一个 shadowRoot
,这个至关因而咱们整个组件的一个 根结点。框架
紧接着,咱们获取到自身的 text
属性,而且将其 倒置 放入新建立的 span
元素中。dom
最后,咱们把带有 text
的 span
塞入 shadowRoot
。函数
定义完成以后,咱们要告知一下系统,也就是 组件注册。组件化
customElements.define(
'text-reverse',
TextReverse
)
复制代码
这里有一个小细节,就是咱们注册的名字必须是带短横线的。
注册完成以后就能够正式使用啦。
<text-reverse text='12345'></text-reverse>
复制代码
上面的例子中,咱们用到了 shadow root,他承载着咱们组件全部的内容。而他也是 Web Component 核心技术。
咱们都知道 Dom 其实就是一棵树,而咱们的组件则是树上的一个节点。咱们能够称组件节点为 shadow host。
shadow host 中含有一颗与外界隔离的 dom 树,咱们称之为 shadow tree。shadow tree 中的内容不会影响到外界。Shadow Root 则是这一课shadow tree 的根节点。
结构如图所示:
shadow dom 一大亮点就是样式隔离。咱们能够给以前的例子加上样式。
class TextReverse extends HTMLElement {
constructor() {
super();
// ...
const style = document.createElement('style');
style.textContent = `* { background: red; }`
shadowRoot.appendChild(style);
// ...
}
}
复制代码
咱们给全部元素添加一个红的背景色。可是,结果只有组件内的元素背景色受到了影响。这种样式隔离的特性很好地避免了不一样组件之间的样式干扰。
在上面的例子中,咱们采用代码的方式来建立修改节点。相较于 React 的 Jsx 和 Vue 的模版,这种方法比较低效。因此,咱们可使用 Template 来解决这问题。
<template id='text-reverse'>
<style> *{ background: red; } </style>
<span id='text'></span>
</template>
复制代码
class TextReverse extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
const text = this.getAttribute('text') || '';
const template = document.getElementById('text-reverse').content.cloneNode(true);
template.getElementById('text').textContent = text.split('').reverse().join('');
shadowRoot.appendChild(template);
}
}
复制代码
咱们在 html 中定义了一个 template,而后,就和操做普通元素同样获取到 template 节点,而后深拷贝一份节点内容。最后直接操做这个节点。
和 Vue 的 Slot 类似,Slot 赋予了组件更高的可扩展性。经过 Slot,咱们能够给组件传入更多的自定义内容。
在上面的例子中,咱们给组件添加一个自定义的标题。
<text-reverse text='12345'>
<span slot='title'>text reverse</span>
</text-reverse>
<template id='text-reverse'>
<h1><slot name='title'>default title</slot></h1>
<span id='text'></span>
</template>
复制代码
模版中,咱们定义一个 slot 元素,命名为 title,而且设置一个无内容时的默认值 default title。 使用的时候,咱们在元素中添加一个 slot 属性来与模版中的 slot 相匹配。
至今,咱们都是彻底自定义组件内容,假如咱们想扩展示有系统元素,那就须要定义一个 内置自定义元素。 咱们来用一个屏蔽数字的 p 元素来讲明。
class PFilter extends HTMLParagraphElement {
constructor() {
super();
const textContent = this.textContent;
this.textContent = textContent.replace(/\d/g, '*');
}
}
customElements.define(
'p-filter',
PFilter,
{
extends: 'p'
}
)
复制代码
咱们这边再也不是继承 HTMLElement
,而是继承须要扩展的 p节点 HTMLParagraphElement
。
<p is='p-filter'>个人手机号是:10086</p>
复制代码
不一样于独立自定义组件,咱们仍是须要用原有元素名去声明,而且在 is
属性中填写咱们的组件名。
和大多数框架同样,Web Component 也含有许多控制组件生命周期的方法。
咱们只需在定义组件的类中声明对应的方法便可。attributeChangedCallback
相对与别的属性比较特别,他须要 搭配 observedAttributes
使用。
class TextReverse extends HTMLElement {
//...
static get observedAttributes () {
return ['text'];
}
attributeChangedCallback () {
const text = this.getAttribute('text') || '';
this.shadowRoot.getElementById('text').textContent = text.split('').reverse().join('');
}
}
复制代码
咱们在 observedAttributes
静态方法中添加须要监听的属性值。而后,在 text
改变的时候,触发 attributeChangedCallback
方法来更新 text
的值。
Web Component 的功能十分强大,相较于 React,Vue等框架,他天生自带样式隔离,而且最主要的是拥有浏览器的原生支持。不过,想要达到工程开发标准 的话,他还有一段很长很长的路要走。