Web Componnets

156 阅读4分钟
家里毛孩子佳佳,大名叫 Java

什么是Web Components

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。 以上是 MDN 中的解释,通俗点讲就是允许开发人员创建可重用的自定义组件,封装后就是一个全新的 HTML 标签,可以像使用 div 标签一样在 HTML 中直接调用。

话不多说,上例子,大家都认识的video标签就是 Web Components 实现的:

从图中可以看到,一个拥有进度条、开始、暂停等丰富功能的 video 标签,原来是一堆基础标签拼出来的。并且可以选中 video 标签,在控制台执行 $0.querySelector('.closed');返回结果是 null, 这就是 shadow DOM 的魅力,天然沙盒。

shadow DOM 又是什么,不要觉得神奇,又是什么新概念,只是web Componnet 的一部分而已。

示例: <video src="http://vjs.zencdn.net/v/oceans.mp4"></video>

为什么我的 video 标签下没有 #shadow-root?

打开 Chrome devTool, 控制面板右上角三个点 - Setting - Preferences - Elements - Show user agent shadow DOM 勾选即可, 后边自定义标签练习也要用到,建议勾选


Web Components 三大技术

查看代码请点击展开
<card-info></card-info>

    <script>
        class Info extends HTMLElement {
            constructor() {
                super();

                const template = document.createElement('template');
                template.innerHTML = `
                    <style>
                        img {
                            width: 300px;
                        }
                        div {
                            width: 300px;
                            background-color: #0040ff12;
                            color: #113b91;
                            height: fit-content;
                            background-image: linear-gradient(0deg, #dfd2d2 10%, transparent 0), linear-gradient(90deg, #dfd2d2 10%, transparent 0);
                            background-size: 10px 10px;
                            font-weight: bold;
                            font-size: 20px;
                            box-shadow: inset 0 0 8px 4px #98a8c9;
                            padding: 20px 20px 25px;
                            box-sizing: border-box;
                        }
                    </style>
                    <img src="你家小可爱的照片,如果没有,那你就是小可爱.webp" />
                    <div>hello Web Componnets</div>
                `;

                const shadowRoot = this.attachShadow({ mode: 'open' });
                shadowRoot.appendChild(template.content.cloneNode(true));   
            }
        };

        customElements.define('card-info', Info);
    </script>

没错,这么几行代码已经涵盖了 Web Componnet 中的三项技术,以下是 MDN 对这三部分内容的介绍🎈

  • Custom elements(自定义元素): 一组 JavaScript API,允许您定义 custom elements 及其行为,然后可以在您的用户界面中按照需要使用它们。
  • Shadow DOM(影子 DOM) :一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
  • HTML templates(HTML 模板):  <template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

重点介绍 shaodow DOM

详细文档请移步 MDN,贴一下 MDN 对 shadow DOM 的结构图。

MDN shadow DOM

shadow DOM 的特点

  • css、js 与外界隔离,互不影响,天然沙盒,在微前端场景应用

Web Components 的优势

  • 不受框架的限制,原生 api 支持
  • 对全局污染较小

Web Components 的应用及支持

  • 腾讯 Omi 前面拥抱 Web Components ,融合 jsx 造就了一个新的框架

  • Hybrids: 需要注意的是,Hybrids 是用普通函数定义的组件,而非上边我们示例中的 class,好处呢,就是避免了 this 的限制。

  • React 对 Web Components 的支持:zh-hans.reactjs.org/docs/web-co…

    React 支持在 Web Components 中使用 React,或者在 React 中使用 Web Components,或者两者共存。

    在 React 中使用 Web Componnets

    class HelloMessage extends React.Component {
      render() {
        return <div>Hello <x-search>{this.props.name}</x-search>!</div>;
      }
    }
    

    在 Web Componnets 中使用 React

    class XSearch extends HTMLElement {
      connectedCallback() {
        const mountPoint = document.createElement('span');
        this.attachShadow({ mode: 'open' }).appendChild(mountPoint);
    
        const name = this.getAttribute('name');
        const url = 'https://www.google.com/search?q=' + encodeURIComponent(name);
        const root = ReactDOM.createRoot(mountPoint);
        root.render(<a href={url}>{name}</a>);
      }
    }
    customElements.define('x-search', XSearch);
    
  • Vue 对 Web Components 的支持:

    我们认为 Vue 和 Web Components 大体上是互补的技术。Vue 能很好地解析和创建自定义元素。不论是在将自定义元素整合到已有的 Vue 应用中,还是使用 Vue 构建和分发自定义元素,你都能获得很好的支持。

    import { defineCustomElement } from 'vue'
    
    const MyVueElement = defineCustomElement({
      // 在此提供正常的 Vue 组件选项
      props: {},
      emits: {},
      template: `...`,
    
      // defineCustomElement 独有特性: CSS 会被注入到隐式根 (shadow root) 中
      styles: [`/* inlined css */`]
    })
    
    // 注册自定义元素
    // 注册完成后,此页面上的所有的 `<my-vue-element>` 标签会被更新
    customElements.define('my-vue-element', MyVueElement)
    
    // 你也可以编程式地实例化这个元素:
    // (只能在注册后完成此操作)
    document.body.appendChild(
      new MyVueElement({
        // initial props (optional)
      })
    )
    

有时间可看看

  • slot 增加 Template 灵活度,用法如下: 可对比 React 的 Render Props, vue 的 slot用法

// template  自定义标签名:my-paragraph
<tmeplate>
    <p title="我是 template 功能标签">
        <slot name="my-text" title="我是外部传入dom, 传什么画什么,与 template 无关">My default text</slot>
    </p>
</template>

// my-paragraph 自定义标签消费
<my-paragraph>
  <span slot="my-text">Let's have some different text!</span>
</my-paragraph>
  • Custom elements 生命周期 上文中示例只做了最简单的介绍,自定义元素内部是简单的无变化的内容,生命周期可以使其活起来。定义在自定义元素的类定义中的特殊回调函数,影响其行为:

  • connectedCallback: 当自定义元素第一次被连接到文档 DOM 时被调用。

  • disconnectedCallback: 当自定义元素与文档 DOM 断开连接时被调用。

  • adoptedCallback: 当自定义元素被移动到新文档时被调用。

  • attributeChangedCallback: 当自定义元素的一个属性被增加、移除或更改时被调用。