前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >十分钟带你入门 Web Components

十分钟带你入门 Web Components

作者头像
GopalFeng
发布2022-08-01 20:09:20
1.8K0
发布2022-08-01 20:09:20
举报
文章被收录于专栏:前端杂货铺-Gopal

什么是 web components

组件封装,是我们前端一直在探讨的话题。但现在我们的组件库,更多的是基于某个框架去实现,比如 Vue 的 ElementUI,React 的 ANTD。这种组件的缺点就是对外部框架的依赖,你必须基于 Vue 或者 React 才能使用,假如某一天项目迁移又得重新书写一套。

那能不能基于原生的 HTML/CSS/JS 就能封装的组件规范呢?那就是 Web Components。

Web Components 本身不是一个单独的规范,而是由一组 DOM API 和 HTML 规范所组成,用于创建可复用的自定义名字的 HTML 标签,并且可以直接在你的 Web 应用中使用。

组件的定义以及核心目标

我认为组件就是内部抽象封装了一定的逻辑功能,并暴露相关接口给外部调用。

它能够完成以下的功能:

  • 复用:组件将会作为一种复用单元,被用在多处。
  • 解耦:组件本身隔离了变化,组件开发者和业务开发者可以根据组件的约定各自独立开发和测试。
  • 封装:组件屏蔽了内部的细节,组件的使用者可以只关心组件的属性和事件和方法。
  • 抽象:组件通过属性和事件、方法等基础设施提供了一种描述 UI 的统一模式,降低了使用者学习的心智成本。

那 Web Components 怎么做到以上几点的呢?

Web Components 的核心概念

主要有以下几点:

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

我们接下来通过封装一个 user-card Web Components 组件实战说明一下。本文 Demo 地址[1]

HTML 模板

我们先定义好它的 HTML 和 CSS。

代码语言:javascript
复制
<template id="userCardTemplate">
  <style>
    :host {
      display: flex;
      align-items: center;
      // 省略部分代码...
    }
    // 省略很多的CSS 代码
  </style>

  <img class="image" />
  <div class="container">
    <slot name="name"></slot>
    <slot name="email"></slot>
    <button class="button">Follow Gopal</button>
  </div>
</template>

这里留意几点:

  • template 可以理解成普通的 div,只是这个元素内的所有内容不会直接展示到界面上。
  • template 标签中可以直接插入 style 标签在,模板内部定义样式。其中 :host 伪类用来定义 shadow-root 的样式,也就是包裹这个模板的标签的样式。
  • 留意占位符。该占位符可以在后期使用自己的标记语言填充,后面我们会提到。它的使用跟 Vue 的插槽是一样的。应该说 Vue 借鉴了它的实现。

Custom elements(自定义元素)

使用 CustomElementRegistry.define() 方法注册您的新自定义元素 ,并向其传递要定义的元素名称、指定元素功能的类、以及可选的其所继承自的元素。

  • 自定义元素的名称,一个 DOMString 标准的字符串,为了防止自定义元素的冲突,必须是一个带短横线连接的名称(e.g. custom-tag)。这个也是 Vue 自定义组件命名推荐的使用方式。
  • constructor。自定义元素构造器,包含组件的生命周期的定义。
  • 扩展参数(可选),该参数类型为一个对象,且需要包含 extends 属性,用于指定创建的元素继承自哪一个内置元素(e.g. { extends: 'p' })。

代码如下:

代码语言:javascript
复制
class UserCard extends HTMLElement {
  constructor() {
    // 必须调用 super();
    super();
    // 创建一个 shadow 节点,创建的其他元素应附着在该节点上
    var shadow = this.attachShadow({ mode: "closed" });
    // 获取到模板实例
    var templateElem = document.getElementById("userCardTemplate");
    // 进行拷贝,因为页面上的模板并不是一次性的,可能其他的组件也要引用
    var content = templateElem.content.cloneNode(true);
    // this.getAttribute 可以获取到组件的传参
    content
      .querySelector("img")
      .setAttribute("src", this.getAttribute("image"));
    // 添加到 shadow dom 中
    shadow.appendChild(content);
  }
}
// 注册自定义元素
window.customElements.define("user-card", UserCard);

这里需要留意这个点:

  • Class 类必须调用 super()。
  • 获取到模板之后,需要通过 clone() 方法进行拷贝,因为页面上的模板并不是一次性的,可能其他的组件也要引用。
  • this.getAttribute 可以获取到组件的传参。

定义好之后,我们就可以直接使用 user-card 这个自定义元素了,并且可以传递属性给组件,并且能够通过 slot 标签指定 name 属性,使用上面 HTML 模板中我们定义好的占位符。

代码语言:javascript
复制
<user-card
  image="https://p3-passport.byteacctimg.com/img/user-avatar/a0383600d66ccc81b3396b75cf3a95ea~300x300.image"
>
  <p slot="name" class="name">Gopal</p>
  <p slot="email" class="email">1172597655@qq.com</p>
</user-card>

最终的组件效果如下:

Shadow DOM

上面的 Demo 中其实已经使用了,我们可以在任意一个节点内部创建一个 Shadow DOM,在获取元素实例后,调用 Element.attachShadow() 方法,就能将一个新的 shadow-root 附加到该元素上。

该方法接受一个对象,且只有一个 mode 属性,值为 open 或 closed,表示 Shadow DOM 内的节点是否能被外部获取。

上面我们设置为 closed

假如改成 open,结果如下:

面临的挑战

虽然 Web Components 提出来已经很久了,但是普及的程度远远没有 Vue、React 这些框架的组件库。其主要的问题是,Vue、React 这些框架帮助我们解决了一些视图渲染的逻辑,比如 React,使用 JSX 和 Css module,我们只需要关心数据状态,不需要像 Web Components 一样需要更多的关注 HTML 模板,也就带来了更多的灵活和便利。

总结与思考

精读《Web Components 的困境》[2]的总结中提到:

Web Components 作为浏览器底层特性不应该拿出来和 React,vue 这类应用层框架相比较。Web Components 的方向以及提供的价值都不会跟 应用框架一致。而 Web Components 作为未来的 Web 组件标准,它在任何生态中都可以运行良好。我倒是更加期待应用层去基于 Web Components 去做更多的实现,让组件超越框架存在,可以在不同技术栈中使用。

确实 React 和 Web Components 也是可以共存的,React 官方文档也提到:

为了解决不同的问题而生。Web Components 为可复用组件提供了强大的封装,而 React 则提供了声明式的解决方案,使 DOM 与数据保持同步。两者旨在互补。作为开发人员,可以自由选择在 Web Components 中使用 React,或者在 React 中使用 Web Components,或者两者共存。

至于应用层去基于 Web Components 去做更多的实现,我觉得这是一个很理想的状态,毕竟要 React、Vue 基于 Web Components 去封装它们的实现,那就需要 Web Components 做更多灵活的规范和标准,期待这一天的到来吧。

参考

  • 精读《Web Components 的困境》[3]
  • Web Components MDN[4]
  • Web Components 上手指南[5]

参考资料

[1]Demo 地址: https://codepen.io/gpingfeng/pen/zYRMagp

[2]精读《Web Components 的困境》: https://juejin.cn/post/6844903494885851149

[3]精读《Web Components 的困境》: https://juejin.cn/post/6844903494885851149

[4]Web Components MDN: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components

[5]Web Components 上手指南: https://segmentfault.com/a/1190000039269731

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端杂货铺 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是 web components
  • 组件的定义以及核心目标
  • Web Components 的核心概念
  • HTML 模板
  • Custom elements(自定义元素)
  • Shadow DOM
  • 面临的挑战
  • 总结与思考
  • 参考
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档