web components 可以使你创建可以复用的自定义元素,将其功能代码进行封装并与其他代码进行区隔。
概念
- custom elements: 使用一组 JS API 来对自定义组件进行定义,并对功能进行封装
- shadow dom: 使用一组 JS API 来将封装好的 shadow dom tree 添加到实际的元素上,它将会被单独的进行渲染并且独立于 document DOM。使用这种方式,可以将自定义元素的特性和样式进行抽象,而且不用担心与 documnet 中的其他部分产生冲突
- HTML templates:
<template>
和<slot>
元素可以使你对模版进行复用和分发
创建一个 customElemnet 的流程
- 使用类来定义你的 web component 的功能
- 使用
CustomElementRegistry.define()
来定义你的 web componet 的名字和实际功能 - 使用
Element.attachShadow()
方法来将 shadow DOM 添加到自定义元素上 -
创建
定义自定义组件的功能 ```javascript // 定义自定义组件功能 class Square extends HTMLElement {
constructor() { super()
const shadow = this.attachShadow({ mode: ‘open’ })
const div = document.createElement(‘div’) const style = document.createElement(‘style’)
shadow.appendChild(style) shadow.appendChild(div) } }
// 定义自定义组件 customElements.define(‘custom-square’, Square)
自定义组件的名字需要是 kebab-case 的,两个单词用连字符进行连接,用来与原生的元素来进行区隔。
```html
<custom-square></custom-square>
使用生命周期回调
我们可以通过在自定义元素的类中创建对应的生命周期回调函数,常用的生命周期如下:
connectedCallback
: 每次自定义元素被添加到 docment 连接的元素中时会进行触发disconnectedCallback
: 自定义元素从 document DOM 中断开连接时进行触发adoptedCallback
: 自定义元素被移到新的 document 的时候会触发attributeChangedCallback
: 自定义元素的属性被添加、移除或修改的时候会进行触发// 更新属性
function updateStyle(elem) {
const shadow = elem.shadowRoot;
shadow.querySelector('style').textContent = `
div {
width: ${elem.getAttribute('length')}px;
height: ${elem.getAttribute('length')}px;
background-color: ${elem.getAttribute('color')};
}
`;
}
// 生命周期回调
class Square extends HTMLElement {
// 对对应属性进行监听
static get observedAttributes() {
return ['color', 'length']
}
// ...
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);
}
}
<custom-square length="100" color="red"></custom-square>
扩展
可以利用 web components 来进行扩展,作为单独的组件来封装成一个库来使用。我们仅需要完成以下的部分:
响应式的状态管理系统,用来进行跨组件的逻辑抽离和复用
- string template
- 声明式的高效的模版系统
- 生命周期
使用 reactive 的功能实现依赖收集,当依赖发生变更的时候会触发模版引擎的重新渲染,调用生命周期的 attributeChangedCallback
回调来获得新的值,并触发重渲染。
详情可以参考 vue-lit
自定义元素现存的待完善的地方:
- slot 妨碍了组件组合
- 使用自定义元素时需要将样式代码耦合到 JS 中然后再添加到 shadow DOM 中,这导致了重复的样式代码