一文读懂Web Component

简介: 由于最近作者在学习微前端,web component也是其中一大特性,部分微前端框架使用到,也是深入学习了一下相关的知识,分享出来。

您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

前言

由于最近作者在学习微前端,web component也是其中一大特性,部分微前端框架使用到,也是深入学习了一下相关的知识,分享出来。

Web Component是什么?

Web Component 实际上一系列技术的组合,主要包含 3 部分:

  • 自定义元素。 在 HTML 基础标签外扩展自定义标签元素,也就是我们平时使用框架的"组件";
  • Shadow DOM。 主要用于将 Shadow DOM 的内容与外层 document DOM 隔离,可以理解为在document中的一个子容器,放置各种组件;
  • HTML 模板。 使用 <template> 来定义组件模板,使用 <slot> 作为插槽使用(Vuer一定不陌生);

在一份html文件中的一个web component看起来是这样的:

<trace-ele name="webComponent" version="0.0.1" desc="原生态自带隔离的组件">
  <div slot="slot-ele">插槽内容</div>
</trace-ele>

看起来很像Vue吧?接下来让我们一个个demo学习web component

上手

由于Web Component亲和原生,因此无需其他包的依赖,一个index.html和一个index.js即可体验学习。

我们直接写一个html模板,文章的案例组件统称为<trace-ele />

index.html:

<body>
 <template id="trace">
      <div class="container">
        <img
          class="image"
          src="https://pic1.zhimg.com/50/v2-a6d65e05ec8db74369f3a7c0073a227a_200x0.webp"
          alt=""
        />
        <p class="title">学习Web Component</p>
        <p class="desc">Web Component是微前端沙盒隔离原理的重要知识</p>
        <p class="price">¥25.00</p>
      </div>
    </template>
    <trace-ele />
    <script src="./index.js" />
</body>

这里我们写了一个"模板"——template,并在下面声明了<trace-ele />组件。

而实现这一切的原理在index.js中。

class Trace extends HTMLElement {
   
   
  constructor() {
   
   
    super();
    const templateEle = document.getElementById("trace");
    const cloneEle = templateEle.content.cloneNode(true);
    this.appendChild(cloneEle);
  }
}

customElements.define("trace-ele", Trace);

Web Component组件本质是一个类继承于HTMLElement,当customElements.define声明完组件后,类中的this指向于组件本身,打印结果如下:

image.png

在初始化时,需要提供给组件一个空壳,并且绑定template元素的id,这样就出现组件效果了。

image.png

看到这里是不是感觉和Vue很像呢?接下来我们继续升级组件的功能~

来点样式吧

在上一节基础上,给组件上点样式,很简单,改变index.html即可,在template中加入style

<body>
 <template id="trace">
      <div class="container">
        <img
          class="image"
          src="https://pic1.zhimg.com/50/v2-a6d65e05ec8db74369f3a7c0073a227a_200x0.webp"
          alt=""
        />
        <p class="title">学习Web Component</p>
        <p class="desc">Web Component是微前端沙盒隔离原理的重要知识</p>
        <p class="price">¥25.00</p>
      </div>

      <style>
        .container {
    
    
          display: inline-flex;
          flex-direction: column;
          border-radius: 6px;
          border: 1px solid silver;
          padding: 16px;
          margin-right: 16px;
        }
        .image {
    
    
          border-radius: 6px;
        }
        .title {
    
    
          font-weight: 500;
          font-size: 16px;
          line-height: 22px;
          color: #222;
          margin-top: 14px;
          margin-bottom: 9px;
        }
        .desc {
    
    
          margin-bottom: 12px;
          line-height: 1;
          font-size: 14px;
        }
        .price {
    
    
          font-size: 14px;
        }
      </style>
    </template>
    <trace-ele />
    <script src="./index.js" />
</body>

样式生效:

image.png

但是这里如果给一个通用标签的样式,就像这样:

<body>
    <p>组件外的P标签</p>
        <template>
        <p>组件中的P标签</p>
        <style>
           p {
    
    
             color: red;
           }
            ...
            .container {
    
    }
        </style>
        </template>
</body>

效果如下:

image.png

可以看到组件外的p标签也被影响了,颜色变为红色,而在组件概念中这个样式其实只期望作用于组件本身。这也是样式隔离的概念,而很幸运,Web Component提供了开箱即用的样式隔离方案。

为了不让 <template> 里的 <style> CSS 和全局的 CSS 有冲突,我们可以将组件挂在到 Shadow Root 上,再用 Shadow Root 挂到外层的 document DOM 上,这样就可以实现 CSS 的隔离啦:

class Trace extends HTMLElement {
   
   
  constructor() {
   
   
    super();
    this.attachShadow({
   
    mode: "open" });

    const templateEle = document.getElementById("trace");
    const cloneEle = templateEle.content.cloneNode(true);

    this.shadowRoot.appendChild(cloneEle);
  }
}

customElements.define("trace-ele", Trace);

从控制台中观察:

image.png

而如果有多个组件本质其实就是在document中有多个Shadow Root

整个DOM架构图是这样的:

image.png

Shadow DOM 的一大优点是能将 DOM 结构、样式、行为与 Document DOM 隔离开,非常适合做组件的封装,因此它能成为 Web Component 的重要组成部分之一。

Props

VueReact一样,Web Component也提供了父传子的形式。

index.html:

<trace-ele name="webComponent" version="0.0.1" desc="原生态自带隔离的组件">

这里传了3个props给组件,在组件中打印this如下:

火眼金睛的我已经找到了在组件中接受传参的入口:

image.png

做一个简单的动态赋值:

class Trace extends HTMLElement {
   
   
  constructor() {
   
   
    super();

    this.attachShadow({
   
    mode: "open" });

    const templateEle = document.getElementById("trace");
    const cloneEle = templateEle.content.cloneNode(true);
    cloneEle.querySelector('.container > .title').textContent = this.getAttribute('name');
    cloneEle.querySelector('.container > .price').textContent = this.getAttribute('version');
    cloneEle.querySelector('.container > .desc').textContent = this.getAttribute('desc');

    this.shadowRoot.appendChild(cloneEle);
  }
}

customElements.define("trace-ele", Trace);

搞定~

Slot

HTML 模板的另一个好处是可以像 Vue 一样使用 <slot>。比如,现在我们可以在这个 <trace-ele> 最下面添加一个插槽:

<body>
    <template id="trace">
        <div class="container">
            <p>组件中的P标签</p>
            <img
              class="image"
              src="https://pic1.zhimg.com/50/v2-a6d65e05ec8db74369f3a7c0073a227a_200x0.webp"
              alt=""
            />
            <p class="title">学习Web Component</p>
            <p class="desc">Web Component是微前端沙盒隔离原理的重要知识</p>
            <p class="price">¥25.00</p>
            <slot name="slot-ele"></slot>
        </div>
        <style>
        ...
        </style>
    </template>
    <trace-ele name="webComponent" version="0.0.1" desc="原生态自带隔离的组件">
        <div slot="slot-ele">插槽内容</div>
    </trace-ele>
</body>

这样我们就可以实现自定义插槽内容了。

事件绑定

Web Component也可以给组件中元素或者插槽绑定事件。

class Trace extends HTMLElement {
   
   
  constructor() {
   
   
    super();

    this.attachShadow({
   
    mode: "open" });

    const templateEle = document.getElementById("trace");
    const cloneEle = templateEle.content.cloneNode(true);
    cloneEle
      .querySelector(".container > .title")
      .addEventListener("click", this.onClick);

    this.shadowRoot.appendChild(cloneEle);
  }

  onClick = () => {
   
   
    alert("Click Me!");
  };
}

customElements.define("trace-ele", Trace);

image.png

总结

上面主要给大家分享了一下 Web Component 的一些使用方法。总的来说,Web Component 是一系列 API 的组合:

  • Custom Element:注册和使用组件
  • Shadow DOM:隔离 CSS
  • HTML template 和 slot:灵活的 DOM 结构

它看起来仿佛是现在主流框架的基建实现,框架也正是基于原生的能力实现出一整套的解决方案,就比如Vue的响应式以来追踪、模板语法数据绑定,都是我们希望看到的。

目录
相关文章
|
JavaScript 前端开发 容器
SolidJs尝鲜与Web Component实践造虚拟滚动的轮子
「造轮子」虚拟滚动 + soild + Web Component
1560 1
|
3月前
|
开发框架 JavaScript 前端开发
Web Component -- 即将爆发的原生的 UI 组件化标准
Web Component -- 即将爆发的原生的 UI 组件化标准
|
JavaScript 前端开发 API
web component基础概念及使用
web component基础概念及使用
83 0
|
6月前
|
弹性计算 前端开发 JavaScript
前端新趋势?有了Web Component,还用纠结Vue或React?
Web Component 的概念最早在 2011 年被 Google 提出,并在 2018 年 V1 版本开始被主流浏览器所支持(除了 IE)。
|
Web App开发 JavaScript 前端开发
[译]2023年 Web Component 现状
[译]2023年 Web Component 现状
203 0
|
前端开发
前端知识学习案例-web component
前端知识学习案例-web component
69 0
前端知识学习案例-web component
|
JavaScript 前端开发 API
秒懂 Web Component
哈喽,大家好,我是海怪。 最近不是写了一篇关于京东微前端框架的文章嘛 《初探 MicroApp,一个极致简洁的微前端框架》,里面提到了一个叫 Web Components 的东西。虽然对它早有耳闻,但是一直也没怎么仔细看过,所以就深入了解了一下,今天给大家做个简单分享 :)
秒懂 Web Component
|
JavaScript 前端开发 API
原生javascript组件开发之Web Component实战
作为一名前端工程师,我们每天都在和组件打交道,我们也许基于react/vue使用过很多第三方组件库,比如ant design,elementUI,iView等,或者基于它们进行过组件的二次开发,比如业务组件,UI组件等,亦或者觉得团队能力很强,可以不依赖第三方而独立开发属于自己的组件库。无论何种形式,组件开发已然成为我们工作中的必备技能,为了更好的复用性和可维护性,组件化开发是必然选择,也正是因为组件化开发越来越重要,几年前web标准推出了Web Component这一概念,意在解决html原生标记语言复用性的问题。
979 0
|
Web App开发 JavaScript 前端开发
使用纯粹的JS构建 Web Component
Web Component 出现有一阵子了。 Google 费了很大力气去推动它更广泛的应用,但是除 Opera 和 Chrome 以外的多数主流浏览器对它的支持仍然不够理想。
431 0

热门文章

最新文章