web components 可以使你创建可以复用的自定义元素,将其功能代码进行封装并与其他代码进行区隔。

概念

  • custom elements: 使用一组 JS API 来对自定义组件进行定义,并对功能进行封装
  • shadow dom: 使用一组 JS API 来将封装好的 shadow dom tree 添加到实际的元素上,它将会被单独的进行渲染并且独立于 document DOM。使用这种方式,可以将自定义元素的特性和样式进行抽象,而且不用担心与 documnet 中的其他部分产生冲突
  • HTML templates:<template><slot>元素可以使你对模版进行复用和分发

创建一个 customElemnet 的流程

  1. 使用类来定义你的 web component 的功能
  2. 使用CustomElementRegistry.define()来定义你的 web componet 的名字和实际功能
  3. 使用 Element.attachShadow()方法来将 shadow DOM 添加到自定义元素上
  4. 在页面中使用你的自定义元素

    创建

    定义自定义组件的功能 ```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)

  1. 自定义组件的名字需要是 kebab-case 的,两个单词用连字符进行连接,用来与原生的元素来进行区隔。
  2. ```html
  3. <custom-square></custom-square>

这样我们就可以在 html 中直接使用自己创建的组件。

使用生命周期回调

我们可以通过在自定义元素的类中创建对应的生命周期回调函数,常用的生命周期如下:

  • connectedCallback: 每次自定义元素被添加到 docment 连接的元素中时会进行触发
  • disconnectedCallback: 自定义元素从 document DOM 中断开连接时进行触发
  • adoptedCallback: 自定义元素被移到新的 document 的时候会触发
  • attributeChangedCallback: 自定义元素的属性被添加、移除或修改的时候会进行触发

    1. // 更新属性
    2. function updateStyle(elem) {
    3. const shadow = elem.shadowRoot;
    4. shadow.querySelector('style').textContent = `
    5. div {
    6. width: ${elem.getAttribute('length')}px;
    7. height: ${elem.getAttribute('length')}px;
    8. background-color: ${elem.getAttribute('color')};
    9. }
    10. `;
    11. }
    1. // 生命周期回调
    2. class Square extends HTMLElement {
    3. // 对对应属性进行监听
    4. static get observedAttributes() {
    5. return ['color', 'length']
    6. }
    7. // ...
    8. connectedCallback() {
    9. console.log('Custom square element added to page.');
    10. updateStyle(this);
    11. }
    12. disconnectedCallback() {
    13. console.log('Custom square element removed from page.');
    14. }
    15. adoptedCallback() {
    16. console.log('Custom square element moved to new page.');
    17. }
    18. attributeChangedCallback(name, oldValue, newValue) {
    19. console.log('Custom square element attributes changed.');
    20. updateStyle(this);
    21. }
    22. }
    1. <custom-square length="100" color="red"></custom-square>

    扩展

    可以利用 web components 来进行扩展,作为单独的组件来封装成一个库来使用。我们仅需要完成以下的部分:

  • 响应式的状态管理系统,用来进行跨组件的逻辑抽离和复用

  • string template
  • 声明式的高效的模版系统
  • 生命周期

使用 reactive 的功能实现依赖收集,当依赖发生变更的时候会触发模版引擎的重新渲染,调用生命周期的 attributeChangedCallback回调来获得新的值,并触发重渲染。
详情可以参考 vue-lit
自定义元素现存的待完善的地方:

  • slot 妨碍了组件组合
  • 使用自定义元素时需要将样式代码耦合到 JS 中然后再添加到 shadow DOM 中,这导致了重复的样式代码