Web Components - 基础篇

背景

Web Components 是什么?顾名思义,就是“网页组件”,我们先来看看MDN 上是怎么描述的:

Web Components 是一组 web 平台的技术API ,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web 应用中使用它们。

从 MDN描述上来看,Web Components有以下几个关键词:技术 API可重用、定制

接下来我们详细了解下这项技术对我们前端开发有什么好的改进。

在目前大前端的开发背景下,React、Vue、Angular 等都支持组件化的开发,你可以将前端界面抽离成各种不同的组件,独立封装样式和交互细节,并且可以通过事件/属性的方式跟其他组件进行通信,在传统的 HTML 中我们虽然也可以通过 HTML 标签封装,但是其问题在于会存在样式污染,DOM 结构嵌套混乱,无法隔离等等问题,而Web Components的出现就很好的解决了以上这些问题,可以让我们用原生 Web 技术来实现类似于 React 等组件的包装。

Web Components 的组成

Web Components不是单一的规范,而是一系列的技术组成,包括Custom Element、Shadow DOM、HTML templates 三种技术规范。

Custom elements

一组JavaScript API,允许您定义custom elements及其行为,然后可以在您的用户界面中按照需要使用它们。

Shadow DOM

一组JavaScript API,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。

HTML templates

<templete><slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。

兼容性

关于以上 3 项技术的兼容性,我们可以通过以下链接查阅:

  • https://caniuse.com/?search=custom%20elements
  • https://caniuse.com/?search=shadow%20dom
  • https://caniuse.com/?search=html%20templates

接下来我们具体看看在一个 web components这 3 项技术分别的作用点是什么?

Custom Elements

先抛出代码:

<script>
  class HelloHockor extends HTMLElement {
    constructor() {
      super();
      const shadow = this.attachShadow({
        mode: "open",
      });
      const div = document.createElement("div");
      div.className = "hello";
      // 不会污染外界
      const style = document.createElement("style");

      style.innerHTML = `
        .hello {
          color: red;
        }
      `;
      div.innerHTML = "hello world";
      shadow.appendChild(div);
      shadow.appendChild(style);
    }
  }

  const tagName = "hello-hockor";
  // 判断页面中是否已定义过该 tag
  const isDefined = customElements.get(tagName);
  if (!isDefined) {
    customElements.define(tagName, HelloHockor);
  }
</script>

接下来我们来详细解释下

  • 首先我们定义一个 class Hello World,继承自 HTMLElement
  • 在该 class 的 constructor 里面,我们可以通过 this.attachShadow 来创建一个 shadow dom
  • 在 shadow dom 里面我们可以通过浏览原生的 DOM 方法创建标准 HTML 标签,添加内容
  • 最后我们通过customElements.define 来定义我们输出的标签名。

CustomElementRegistry

CustomElementRegistry接口提供注册自定义元素和查询已注册元素的方法,要获取它的实例,请使用 window.customElements属性。

详细文档地址:https://developer.mozilla.org/zh-CN/docs/Web/API/CustomElementRegistry

我们这里主要使用他来定义一个新的标签,该方法接受以下3个参数:

  • 表示所创建的元素名称的符合 DOMString 标准的字符串。注意,custom element 的名称不能是单个单词,且其中必须要有短横线
  • 用于定义元素行为的
  • 可选参数,一个包含 extends 属性的配置对象,是可选参数。它指定了所创建的元素继承自哪个内置元素,可以继承任何内置元素。

总结

通过以上 JS 的定义,我们就可以在普通 DOM中使用这个 tag 来渲染我们自定义的内容了,这个 tag 是由我们自己控制的,而且如果你打开控制台,你会看到它的内部是被包裹在 shadow-root 里面的,并且其内部的样式并不会影响到外部同类名的样式。

demo 地址:https://g8vtnv.csb.app/1.html

Template

在上面的例子中,我们在shadow 中通过document.createElement的方式创建了我们内部需要的结构,但是这种过程式的写法是非常费力不讨好的,比如你现在需要修改 DOM 的嵌套顺序,你得重新写一大串的 dom 操作,或者是后续你想改个属性,也非常麻烦,还是我们常用的声明式结构方便,所以这里就引出了我们 web components 中的第二个关键内容 - template。

我们先来看看如何在 web components 中使用 template

<div id="app">
  <div class="hello">i'm from browser</div>
  <hello-world></hello-world>

  <!--模板部分-->
  <template id="hello">
    <style>
      .hello {
        color: red;
      }
    </style>
    <div class="hello">hello world</div>
  </template>
</div>
<script>
  class HelloWorld extends HTMLElement {
    constructor () {
      super()
      const shadow = this.attachShadow({
        mode: 'open'
      })
      // 这里我们直接获取页面中的 template
      const div = document.getElementById('hello')
      shadow.appendChild(div.content.cloneNode(true))
    }
  }
  customElements.define('hello-world', HelloWorld)
</script>

这里注意的一下,我们使用Node.cloneNode() 方法将 template 的内容加到 shadow-root中。

demo地址:https://zl1xtk.csb.app/2.template.html

slot

熟悉Vue 的小伙伴可能用过 vue 中的 slot,可以先在模板中占个位置,后续我们再灵活的传入自己定义好的 DOM,而在 template 中,也是支持 slot的,接下来我们看个例子。

<div id="app">
  <div class="hello">i'm from browser</div>
  <hello-world>
    <!--slot 应用-->
    <span slot="username">hockor</span>
  </hello-world>

  <!--模板部分-->
  <template id="hello">
    <style>
      .hello {
        color: red;
      }
    </style>
    <div class="hello">
      hello,
      <slot name="username">world</slot>
    </div>
  </template>

</div>
<script>
  class HelloWorld extends HTMLElement {
    constructor () {
      super()
      const shadow = this.attachShadow({
        mode: 'closed'
      })
      const div = document.getElementById('hello')
      shadow.appendChild(div.content.cloneNode(true))
    }
  }

  customElements.define('hello-world', HelloWorld)
</script> 

上面的 demo 中我们默认定义的 slot 是个 world,但是最终渲染出来的时候会被我们使用的<span slot="username">hockor</span>给代替,甚至你还可以传入一个 嵌套 DOM结构,方便我们使用。

demo 地址:https://zl1xtk.csb.app/3.template-slot.html

Shadow DOM

最后我们再来看看web components 中的 shadow dom,其实我们之前已经用到它了,只是没有正式的介绍而已。

借用 MDN 的介绍:Web components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上

这里,有几个 Shadow DOM 特有的术语:

  • Shadow host:一个常规 DOM节点,Shadow DOM 会被附加到这个节点上。
  • Shadow tree:Shadow DOM内部的DOM树。
  • Shadow boundary:Shadow DOM结束的地方,也是常规 DOM开始的地方。
  • Shadow root: Shadow tree的根节点。

mode 属性

 const shadow = this.attachShadow({
  mode: 'open'
})

我们在上面的例子中创建 shadow 的时候有传递一个 mode,他的可选值是 open / closed

那么有什么区别呢?其实很简单,当我们 mode = closed 的时候,我们通过 this.shadowRoot拿到是个 null,那么就不可以从外部获取 Shadow DOM 了,浏览器中的某些内置元素就是如此,例如<video>,包含了不可访问的 Shadow DOM。

总结

好了,对于 web components 的基础认识我们先到这里,对于一个可在项目中实际使用的组件,我们肯定还需要有生命周期和 CSS 样式定义,后续我们再继续看看 这 2 个问题。

参考链接

https://developer.mozilla.org/zh-CN/docs/Web/Web_Components


在这里插入图片描述

原创不易,欢迎关注公众号《hockor》,查看更多内容~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值