如何编写一个原生 Web Components 组件

当今前端编程中,利用语义化的 html 结合 css 来完一个组件并不是一件难事,这也意味着无论在 React、Vue 中都可以插入,不过它俩不是今天的主角,接下来我将用一个例子来介绍如何封装一个完整的原生 HTML 的 Web Components 组件,让我们开始吧!
HTML结构首先我们来了解下 HTML 中的 <details> 元素,它可以用于创建一个小部件,其中包含仅在小部件处于“打开”状态时才可见的附加信息,<details>元素内可以包含的内容没有任何限制 。

默认情况下,元素创建的小部件<details>处于“关闭”状态(open标签可使其打开) 。通过单击小部件在“打开”和“关闭”状态之间切换,以显示或隐藏标签中包含的附加信息,内部标签 <summary> 元素则可为该部件提供概要 。
一个简单的例子如下:
<details><summary> 不能说的秘密 </summary>藏的这么深,可还是被你发现了</details>details {border: 1px solid #aaa;border-radius: 4px;padding: .5em .5em 0;}summary {font-weight: bold;margin: -.5em -.5em 0;padding: .5em;}details[open] {padding: .5em;}details[open] summary {border-bottom: 1px solid #aaa;margin-bottom: .5em;}
如何编写一个原生 Web Components 组件

文章插图
 
使用语义化 HTML 的优点:页面内容结构更清晰,方便开发者阅读,更利于浏览器的理解与加载,搜索引擎解析与seo优化 。
添加亿点样式【如何编写一个原生 Web Components 组件】原生元素默认的样式很简陋,因此我们需要为其定制一下样式,这块内容我们简单带过,只讲解关键部分,样式内容有省略,具体可以在文末的码上掘金中看到呈现效果 。
.ContentWarning > summary {position: relative;list-style: none; /** 去除默认样式 **/user-select: none;cursor: pointer;/** 为其添加一个斜线背景 **/--stripe-color: rgb(0 0 0 / 0.1);background-image: repeating-linear-gradient(45deg,transparent,transparent 0.5em,var(--stripe-color) 0.5em,var(--stripe-color) 1em);}/** 通过var变量调整悬停时的颜色样式 **/.ContentWarning>summary: hover,.ContentWarning>summary: focus {--stripe-color: rgb(150 0 0 / 0.1);}
如何编写一个原生 Web Components 组件

文章插图
 
封装模板现在我们来把它封装成一个完整的组件,这需要先将 HTML 编写在模板 template 当中,并设置一个 id,如下所示:
<template id="warning-card"><details class="ContentWarning"><summary><strong>?? 注意:</strong> 以下为隐藏内容</summary><slot name="desc"> 藏的这么深,可还是被你发现了 </slot></details></template>熟悉 Vue 的小伙伴应该很容易理解上面的代码,结构很相似,不过网页不会直接渲染它包裹的内容 。此外我们还对此模板设置了一个插槽 slot,后面会讲到它的作用 。
定义组件有了上面封装好的模板,我们就需要在 JS 中定义成可用组件来让其能够被使用,调用 window 下的 customElements.define 方法,第一个参数是传入组件名称,我们定义组件名为: warning-card ,第二个参数传入一个继承了 HTMLElement 的类,在其构造方法当中获取并克隆一个新的 HTML 节点,它会通过 AppendChild 渲染到页面当中 。
window.customElements.define('warning-card',class extends HTMLElement {constructor() {super();var templateElem = document.getElementById('warning-card');var content = templateElem.content.cloneNode(true);this.appendChild(content);}})接着我们就可以在页面中把它当作组件那样使用了:
<warning-card> </warning-card>插槽与传参回头看看上面我们模板中设置的插槽 slot,此时还是没有生效的,我们需要稍微改写一下构造函数中的渲染方式,将 web 组件定义为一个 Shadow DOM,这样构造的是一个可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起的独立元素,并在最后使用 Node.cloneNode() 方法添加了模板的拷贝到 Shadow 的根结点上 。
window.customElements.define('warning-card',class extends HTMLElement {constructor() {super();var template = document.getElementById('warning-card').content;this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));}})


推荐阅读