Web Components - HTML的模块化思想

Web Components

放眼前端技术发展历程,代码复用和模块化是永远绕不开的话题。

我们的 JavaScript,IIFE 模式和原型模式是其逻辑复用的基本能力。近几年 CMD,AMD,CommonJS 等各种规范对 JS 模块化持续加成,ES6 的定版更是官方爸爸的大力加持。

CSS 则有 @import url,更别说Sass、Less、PostCss等功能强大的预编译器了,相比起来 HTML 则就略显惨淡。

当然,在复用和组件化的潮流中 HTML 也不是那么的格格不入,毕竟时下最流行的 Vue,React,Sevlte 都是实现自定义 HTML 组件强有力框架。然而框架各自为阵,语法和使用方式不同,学习成本不同,总不是长久之计。

那么我们来看看 Web Components 有什么不同的思想呢?

Web Component | MDN

一、基础知识

Web Components 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。

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

看到 <template><slot> 是不是很熟悉,没错,Vue 的模板语法正是尤大借鉴了 Web Component 思想, 那么下面我们以一个实例来深入探讨:

二、如何使用

然而事情并没这么简单,仔细查看 SelfComponent 组件会发现我们只是单纯的把 template 内容挂在 body 上,一点也不优雅;而且说好的 shadow DOM 呢,咋也没见着。

我们思考以下两个问题:

a.怎么把组件脱离真实 DOM,做到内部独立?

b.组件如何做到内容自定义?

三、shadow DOM 改造

针对第一个问题,其核心是希望自定义组件做到内部独立(样式,事件等),借用 shadow 可以实现这一目标。定义 shadow DOM 只需要一个方法即可:Element.attachShadow(shadowRootInit) ,其中 shadowRootInit 是一个字典对象,包含两个值:mode 和 delegatesFocus;具体内容详见 attachShadow

我们将刚才的组件进行改造:

scala 复制代码
class SelfComponent extends HTMLElement {
    constructor() {
        super();
        let shadow = this.attachShadow({ mode: 'open' });
        shadow.appendChild(art.content.cloneNode(true));
    }
}

我们借助了 attachShadow 创建了一个 shadowRoot 对象,然后把模板内容挂在 shadowRoot 上。上面说过 shadowRoot 有私有性,下面我们验证一下:

可以看到自定义元素外的 article 并没有应用上边框和背景样式效果。

四、template 和 slot 的妙用

上面的自定义组件还没有实现内容自定义,那么这时就要请出我们的 slot 插槽。插槽具有一个 name 属性作为标识:

可以发现内容已经可以自定义了!

五、Web Components 进阶

通过上面简单的例子,我们对 Web Compoents 的基本功能大致掌握了。但上面的例子很多简单,不能满足我们复杂的需求,还会有以下问题:

  • 想随意操作模板的内容,给它添加事件啥的。。。
  • 自定义组件确实有了,但我想在组件不同的阶段做不同的事情,怎么搞呢?
  • slot 看着挺有用,但它使用规则定义的太死了,不太灵活

Custom elements

前文我们只讲到如何创建一个自定义元素,然后将其挂载到真实 DOM 或者 shadow DOM 上。接下来进一步看看它还有哪些其他特别的功能。

1.定义和使用

你可以通过 CustomElementRegistry.define() 返回的绑定在 window 对象上的 customElements 实例进行元素自定义。其语法为 customElements.define(name, constructor, options);

  • name : 元素名。只能用短线连接的英文字符,不能是单个单词。例:name='my-comp'
  • constructor:元素构造器。必须继承自 HTMLElement 或其子类(例如:HTMLParagraphElement 等)
  • options:【可选】控制元素如何定义,只支持一个选项 extends,用于指明定义的元素继承自何种类型元素。(例如:{ extends: 'p' })

前两个参数应该比较好理解,第三个参数特别要注意:extends 指明的元素和 constructor 继承的元素保持一致

2.生命周期

在custom element的构造函数中,可以指定多个不同的回调函数,它们将会在元素的不同生命时期被调用:

  • connectedCallback:当 custom element 首次被插入文档DOM时,被调用。
  • disconnectedCallback:当 custom element 从文档DOM中删除时,被调用。
  • adoptedCallback:当 custom element 被移动到新的文档时,被调用。
  • attributeChangedCallback: 当 custom element 增加、删除、修改自身属性时,被调用

具体的使用方法可以参考 MDN

3. Shadow DOM WebComponents最重要特性,它才真正实现了 HTML 的复用 ------ 将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离。我们元素查看Web Components 组件: shadow-root 节点,在此节点内部的样式和行为都不会影响外面元素。

还有一点要特别注意的事,不是任何类型的元素都可以附加到 shadow root 下面。出于安全考虑,目前只有这些类型元素可以附加到 shadow DOM 上(有效的自定义元素也可):可附加的元素

4. template 和 slot

插槽分为具名和非具名, 使用方法和我们的Vue的基本一致, 这里就不赘述了。

上面说了那么多, 发现好像并不能让我们在各种需求中用到, 那么我们下面就了解下在Vue中如何使用Web Components。

六、Web Components + Vue

默认情况下,Vue 会将任何非原生的 HTML 标签优先当作 Vue 组件处理,而将"渲染一个自定义元素"作为后备选项。这会在开发时导致 Vue 抛出一个"解析组件失败"的警告。要让 Vue 知晓特定元素应该被视为自定义元素并跳过组件解析,我们可以指定 compilerOptions.isCustomElement 这个选项

可以看到Web Component已经渲染出来, 要注意的是, CustomElement 好像不能使用 setup 语法糖。

相关推荐
安冬的码畜日常1 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
小白学习日记2 小时前
【复习】HTML常用标签<table>
前端·html
丁总学Java2 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele2 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
DOKE3 小时前
VSCode终端:提升命令行使用体验
前端
xgq3 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试
用户3157476081353 小时前
前端之路-了解原型和原型链
前端
永远不打烊3 小时前
librtmp 原生API做直播推流
前端