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 语法糖。

相关推荐
一斤代码5 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子5 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年5 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子6 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina6 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路7 小时前
React--Fiber 架构
前端·react.js·架构
伍哥的传说7 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409197 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app
我在北京coding7 小时前
element el-table渲染二维对象数组
前端·javascript·vue.js
布兰妮甜7 小时前
Vue+ElementUI聊天室开发指南
前端·javascript·vue.js·elementui