大家好,这里是大家的林语冰。坚持阅读,自律打卡,每天一次,进步一点。
免责声明
本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Getting into web components - an intro。
本期共享的是 ------ 今年对于 WC(Web Components,Web 组件)而言兹事体大,本人对此感到鸡冻。说实话,不久前本人对 WC 几乎一无所知,因为私以为 WC 只是网络上不值一提的蜉蝣之物。但 WC 正在引起更多话题。所以我想更多地了解 WC。
WC 是什么鬼物?
WC 就像网站的构建块,甚至可能是构建块组。
我们可以自定义元素,比如 <my-button>
或 <for-love>
,而不是循规蹈矩地使用 <div>
或 <button>
等标准标签。自定义元素代表我们可以在网站或网络 App 中使用和重用的独特组件。自定义元素内部始终需要有一个连字符,用来区分其与标准 HTML 元素,并遵循 WC 规范设置的命名约定。
连字符背后的主要原因是为了避免冲突,减少与标准 HTML 元素发生命名冲突的机会。由于标准 HTML 元素不包含连字符(当然,现在永远不会包含连字符)。
Shadow DOM
Shadow DOM 就像一个 Web 元素的"密室"。假设我们有一个工作区,我们正在其中构建网站的不同部分。某些部分需要在不影响其余部分的情况下完成工作。Shadow DOM 提供了一个封装的空间来实现这一点。这个"密室"内的元素可以有自己的样式、脚本和结构,而不会干扰主页或其他元素的样式和脚本。我们所指的其他元素就是我们所说的"Light DOM"。
html
<div>
<p>This is content outside the Shadow DOM, thus "Light DOM".</p>
<my-element></my-element>
</div>
<script>
// 创建一个新的自定义元素
class MyElement extends HTMLElement {
constructor() {
super()
// 为自定义元素创建 Shadow DOM
const shadow = this.attachShadow({ mode: 'open' })
// 在 Shadow DOM 里创建 p 标签
const paragraph = document.createElement('p')
paragraph.textContent = 'Shadow DOM 里的内容'
// 将 p 标签添加到 Shadow DOM
shadow.appendChild(paragraph)
}
}
// 定义自定义元素
customElements.define('my-element', MyElement)
</script>
Shadow DOM 有某些具体的特征:
封装:在 Shadow DOM 里,我们的样式和脚本不会受到外界的影响。此行为有助于防止样式意外与网页上其他元素混淆。除了自定义属性之外,外部 CSS 不会对其产生任何影响。
组合:Shadow DOM 引入了插槽,即迷你占位符,我们可以在其中插入自定义内容。这意味着,我们可以自定义元素的内部,而不会破坏原始设计。
举个栗子,我们将在 WC 内插入 <h2>
和 <p>
。它唯一要做的就是,在它们之间放置一个 <hr>
。
html
<my-card>
<!-- Content placed inside the slots -->
<h2 slot="title">Card Title</h2>
<p slot="content">This is the content of the card.</p>
</my-card>
<script>
class MyCard extends HTMLElement {
constructor() {
super()
// 为自定义元素创建 Shadow DOM
const shadow = this.attachShadow({ mode: 'open' })
// 为卡片内容创建容器
const cardContainer = document.createElement('div')
// 为标题创建插槽
cardContainer.innerHTML = `
<slot name="title"></slot>
<hr>
<slot name="content"></slot>
`
// 将卡片容器添加到 Shadow DOM
shadow.appendChild(cardContainer)
}
}
customElements.define('my-card', MyCard)
</script>
现在想象一下 JS 失败了,页面仍然会显示 WC 内部提供的插槽内容,因为这只是一些轻量级 DOM,唯一缺少的是 <hr>
。即使我们必须支持不支持此功能的浏览器,它仍然可能是一个渐进增强。
可访性:可访问性是一种共享语言。Shadow DOM 里的元素可以理解并使用与外部元素相同的辅助语言。更重要的是,它确实可以辅助我们处理某些更难访问的用例,比如选项卡。
关注点分离:Shadow DOM 可让我们将结构、样式和行为整齐地排列在各自的分区中。想象一下,拥有一个用于所有这些重复任务的 WC 库:可访问的选项卡、图像缩放和可动画的下拉菜单。我们仍然可以使用自定义属性来设置样式,因为这些属性会渗透到 WC 中。
::part
伪元素:它允许我们从 Shadow DOM 外部设置 WC 的特定命名部分的样式。
举个栗子:
html
<my-element>
<h2 slot="header">头部内容</h2>
<p slot="content">主内容</p>
</my-element>
这就是我们的 WC 的样子(粉丝请注意 innerHTML
的属性):
js
class MyElement extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
shadow.innerHTML = `
<style>
div {
border: 3px solid blue;
}
</style>
<div part="header"><slot name="header"></slot></div>
<hr/>
<div part="content"><slot name="content"></slot></div>
`
}
}
customElements.define('my-element', MyElement)
现在解释这一点的最好方法是用边框将其可视化。默认情况下,影子 DOM 内的 <div>
元素具有蓝色边框。我们想要为包裹标题槽的 <div>
提供绿色边框,我们可以访问它:
css
/* h2 默认样式 */
h2 {
margin-block: 30px;
color: red;
border: 3px solid red;
}
/* 头部的绿色边框 */
my-element::part(header) {
border: 3px solid green;
}
WC 的开发体验
像这样编写 WC 并不是一种很好的开发人员体验......至少在我看来。对于基本示例而言,它似乎工作得很好,但我只是喜欢编辑器中的一些结构和颜色,而不是仅仅将每个输出放入字符串中。
我们可以使用 Lit 库来辅助创建 WC。Lit 库旨在使 WC 的使用更轻松高效。它提供了若干优点,只需以正常方式编写它们就可以减轻一些负担。
Lit 提供了一种更具声明性的语法来定义组件。使用此方法可以使我们的代码更易于理解和管理,而不是直接处理 WC API 时所需的更复杂的样式。它还具有这个简洁的模板系统,结合了 HTML 和 JS 模板字符串,使处理动态内容更加方便。这不仅有助于我们的编辑器中出现某些漂亮的颜色,还有助于其更新策略,即只更新 DOM 中已更改的部分。与手动更新相比,这可以提高性能。
综合考虑所有这些因素,如果您认真对待 WC,我想说值得一看,因为这个库的大小只有 6kb 左右。
本期话题是 ------ 您开始使用 Web Components 了吗,有遭遇什么痛点吗?
欢迎在本文下方群聊自由言论,文明共享。谢谢大家的点赞,掰掰~
《前端 9 点半》每日更新,坚持阅读,自律打卡,每天一次,进步一点。