当前位置:   article > 正文

web component指南

web component

web component

什么是web component?

web component是web原生提供的封装组件的方式,让开发者定义一些可重复使用的自定义元素。主要包含custom elementsshadow domhtml templates部分,分别用于注册自定义元素、提供shadow-dom接口,为自定义元素的样式和脚本提供一个隔离的环境、通过templateslot编写自定义元素的结构模板。

如何开发一个web component?

一个web component就是一个我们可以在正常dom中使用的自定义元素。自定义元素只需要包含标记结构、样式以及脚本三个部分,最后再对新的自定义元素进行一个注册。

首先是注册,通过CustomElementRegistry.define()方法完成,CustomElementRegistry包含自定义元素的相关功能,可以通过Window.customElements访问它的引用。例如:

customElements.define('element-a', ElementA, { extends: 'p' })
  • 1

可以看到define函数包含三个参数:

1.自定义元素名
2.包含自定义元素内容的类
3.可选参数,包含extends属性的对象,指定了创建的元素继承自哪个内置元素,可以继承任意的内置元素

第三个可选参数,需要解释下,我们注册的自定义元素分为两类。

第一种是独立的元素,不继承其他内置的html元素,此时,define函数不传递第三个参数,元素的类继承自HTMLElment类。这样注册的自定义元素,使用方式简单,像普通的html元素一样,例如:<element-a />

第二种是继承自某个内置的html元素,例如p。此时,第三个参数应该是{ extends: 'p' }。同时元素的类也应该继承HTMLParagraphElement类。使用方式是<p is="element-a" />或者在js中document.createElement("p", { is: "element-a" })

其中自定义元素的类是最重要的部分,我们会在内部定义它的结构、样式和行为。常见结构如下:

class ElementA extends HTMLElement {
    constructor() {
        // 必须首先调用 super 方法
        super();

        // 元素的功能代码写在这里
        // ...
    }

    connectedCallback() {
        // 自定义元素首次插入文档dom时调用
        // 相较于constructor只会执行一次,这个生命周期每次将节点连接到dom时都会调用
        // 可能会执行多次(比如同一个自定义元素remove, append多次)
    }

    disconnectedCallback() {
        // 自定义元素从文档中删除时,调用
    }

    adoptedCallback() {
        // 自定义元素移动到新的文档
        // 比如使用 adoptNode 方法在多iframe下移动元素
    }

    attributeChangedCallback(name, oldVal, newVal) {
        // 属性变更时调用:三个参数对应属性名,旧值,新值
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在自定义元素类中定义内容需要借助shadow-dom接口的能力,它提供了封装的能力,将标记结构、样式和行为隐藏到一个独立的dom中,我们操作shdaow-dom和操作常规dom一样(新添加子节点、修改属性等),但是在shadow-dom中的元素和外部的元素互不影响,不能通过document.querySelector方法拿到内部的元素,外部的css样式也不会影响shadow-dom中定义的元素。我们会在里面定义自定义元素的结构,最后将其附加到常规的dom树中。

浏览器内置的一些html元素也有使用它的功能,例如:video input-range等,在 Chrome 中,开发者可以启用开发者工具的Show user agent shadow DOM选项,就能看到这些隐藏的结构。

在这里插入图片描述
在这里插入图片描述

shadow-dom和dom的关系如下图所示:

在这里插入图片描述

  • shadow host:一个常规的dom节点,是shadow-dom的挂载节点,比如我们的自定义元素
  • shadow tree:shadow-dom内部的dom树
  • shadow boundary:shadow-dom结束的地方,也是常规dom开始的地方
  • shadow root: shadow-tree的根节点

在自定义元素中我们通过Element.attachShadow将一个shadow root附加到任意元素上:

const shadowRoot = elementRef.attachShadow({mode: 'open'})
// 参数mode有两个值
// open表示可以通过element.shadowRoot访问shadow-dom
// closed表示不可以从外部获取shadow dom(例如内置元素video)
  • 1
  • 2
  • 3
  • 4

接着我们就可以为自定义元素添加内容:

const para = document.createElement('p')
para.innerHTML = '这是我的自定义元素'
shadowRoot.appendChild(para)
  • 1
  • 2
  • 3

然后可以使用多种方式给shadow-dom添加样式:

1.创建style元素

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

style.textContent = `
p {
    color: #ccc;
}
`
shadowRoot.appendChild( style )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.通过link元素使用外部的样式

const linkElem = document.createElement('link')
linkElem.setAttribute('rel', 'stylesheet')
linkElem.setAttribute('href', 'style.css')

shadow.appendChild(linkElem)
  • 1
  • 2
  • 3
  • 4
  • 5

3.使用template,在template中加入一个style节点

最后也可以像常规dom一样设置一些交互事件,甚至定制一些专属的属性,例如我们自定义一个card元素,通过text属性指定card的内容:

<custom-card text="这是我的card组件" />

<script>
    class Card extends HTMLElement {
        constructor() {
            super()
            const shadowRoot = this.attachShadow({ mode: 'open' })
            const text = this.getAttribute('text')
            const p = document.createElement('p')
            p.innerHTML = text
            shadowRoot.appendChild(p)
        }
    }
    customElements.define('custom-card', Card)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

最后介绍下模板和slot的使用,上面的方式定义样式和页面结构太麻烦,也不利于复用,我们可以切换为模板和slot的方式,

我们可以先在template中定义内容和样式,然后添加到shadow dom中:

class extends HTMLElement {
    constructor() {
        super();
        let template = document.getElementById('element-tmp')
        let templateContent = template.content

        const shadowRoot = this.attachShadow({mode: 'open'})
            .appendChild(templateContent.cloneNode(true))
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

另外开发者可以通过slot插槽的方式来传递自定义组件需要的内容(类似于vue),灵活度更高,同时插槽中的内容会受到外部样式的影响。插槽分为命令插槽和默认插槽。

// 相同名称的多个插槽内容会被放在一起
<slot-exp>
    <div>未命名插槽内容1</div>
    <div>未命名插槽内容2</div>
    <div slot="content-a">content-a</div>
    <div slot="content-a">content-a</div>
</slot-exp>

<template id="slot-exp--tmp">
    <div class="slot-exp">
        <div>
            <h2>未命名插槽:</h2>
            <slot />
        </div>
        <div>
            <h2>具名插槽:</h2>
            <slot name="content-a" />
        </div>
        <slot name="content-b">插槽 content-b 默认填充</slot>
    </div>
</template>

<script>
    class SlotExp extends HTMLElement {
        constructor() {
            super()

            this.attachShadow({ mode: 'open' })
            const tmp = document
                .getElementById('slot-exp--tmp')
                .content
            this.shadowRoot.appendChild( tmp.cloneNode(true) )
        }
    }
    customElements.define('slot-exp', SlotExp)
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

web component api的兼容性

开发者可以到can i use搜索web component查看支持程度,支持度还不错。对于一些还未支持的浏览器,也有polyfills使用。

总结

相较于目前流行的react、vue、ng等前端框架,web component是浏览器原生提供的不添加任何依赖的组件封装方式,同时提供了一个隔离的运行环境,目前流行的微前端框架的实现方式之一就是借助了web component的隔离能力。还可以到https://www.webcomponents.org/查看一些开源的web component

thx

  • https://developer.mozilla.org/zh-CN/docs/Web/Web_Components
  • https://javascript.info/slots-composition
  • https://zhuanlan.zhihu.com/p/42370005
  • https://juejin.im/post/6844903876127113230
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/blog/article/detail/93223
推荐阅读
相关标签
  

闽ICP备14008679号