前言
在 Web 开发中,我们经常会遇到需要复用的 UI 组件,如弹窗、下拉框等。传统的实现方式是使用模板或框架提供的组件,但是这些组件往往过于笨重,难以满足个性化需求。Web Components 规范的出现,为我们提供了一种更加灵活、轻量的组件开发方式。
Web Components 规范包含四个部分:Custom Elements、Shadow DOM、HTML Templates 和 HTML Imports。本文将重点介绍 Custom Elements 部分的解读与实践。
Custom Elements
Custom Elements 允许开发者创建自定义元素,并且可以通过 JavaScript 来定义它们的行为。这些自定义元素可以像原生 HTML 元素一样使用,并且可以被其他开发者复用。
定义 Custom Elements
定义一个 Custom Element 首先需要继承 HTMLElement 类,然后通过 customElements.define() 方法来注册元素。例如,我们定义一个名为 my-button 的 Custom Element:
// javascriptcn.com 代码示例 class MyButton extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <style> button { background-color: #007aff; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; font-size: 16px; cursor: pointer; } </style> <button><slot></slot></button> `; } } customElements.define('my-button', MyButton);
在上面的代码中,我们定义了一个名为 MyButton 的类,继承自 HTMLElement 类。在构造函数中,我们首先调用了 super(),然后通过 attachShadow() 方法创建了一个 Shadow DOM,并且设置了 mode 为 open,表示可以从外部访问 Shadow DOM。接着,我们通过 innerHTML 属性设置了 Shadow DOM 的内容,包括了一个样式和一个按钮,按钮中包含了一个 slot,用来接受插入的内容。
最后,我们通过 customElements.define() 方法将 MyButton 类注册为 my-button 元素,这样就可以在 HTML 中使用它了:
<my-button>Click me!</my-button>
生命周期回调
Custom Elements 还提供了一些生命周期回调函数,用于在元素的生命周期中执行一些操作。这些回调函数包括 connectedCallback、disconnectedCallback、attributeChangedCallback 和 adoptedCallback。
- connectedCallback:当元素被插入到文档中时调用。
- disconnectedCallback:当元素从文档中移除时调用。
- attributeChangedCallback:当元素的属性发生变化时调用。
- adoptedCallback:当元素被移动到新的文档时调用。
例如,我们可以在 connectedCallback 回调函数中添加一个点击事件监听器:
// javascriptcn.com 代码示例 class MyButton extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <style> button { background-color: #007aff; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; font-size: 16px; cursor: pointer; } </style> <button><slot></slot></button> `; } connectedCallback() { this.shadowRoot.querySelector('button').addEventListener('click', () => { alert('Button clicked!'); }); } } customElements.define('my-button', MyButton);
属性与属性观察器
Custom Elements 还支持定义属性,并且可以通过属性观察器来监听属性的变化。例如,我们可以给 MyButton 添加一个 disabled 属性,并且在属性变化时修改按钮的 disabled 状态:
// javascriptcn.com 代码示例 class MyButton extends HTMLElement { static get observedAttributes() { return ['disabled']; } constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <style> button { background-color: #007aff; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; font-size: 16px; cursor: pointer; } </style> <button><slot></slot></button> `; } get disabled() { return this.hasAttribute('disabled'); } set disabled(val) { if (val) { this.setAttribute('disabled', ''); } else { this.removeAttribute('disabled'); } } attributeChangedCallback(name, oldValue, newValue) { if (name === 'disabled') { this.shadowRoot.querySelector('button').disabled = this.disabled; } } } customElements.define('my-button', MyButton);
在上面的代码中,我们通过 static get observedAttributes() 方法定义了一个 observedAttributes 静态属性,用于定义需要观察的属性。接着,我们定义了一个 disabled 属性,通过 get 和 set 方法来获取和设置 disabled 属性的值。在 attributeChangedCallback 回调函数中,我们监听 disabled 属性的变化,并且根据新值修改按钮的 disabled 状态。
实践
下面我们通过一个实例来演示如何使用 Custom Elements 来开发一个复用性高的 UI 组件。
我们先定义一个名为 my-modal 的 Custom Element,用于实现弹窗功能:
// javascriptcn.com 代码示例 class MyModal extends HTMLElement { static get observedAttributes() { return ['visible']; } constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <style> :host { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 9999; background-color: rgba(0, 0, 0, 0.5); } :host([visible]) { display: block; } .modal { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #fff; border-radius: 4px; padding: 16px; } .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; } .title { font-size: 20px; font-weight: bold; } .close { color: #999; font-size: 24px; cursor: pointer; } .content { margin-bottom: 16px; } .footer { display: flex; justify-content: flex-end; } .button { background-color: #007aff; color: #fff; border: none; border-radius: 4px; padding: 8px 16px; font-size: 16px; cursor: pointer; margin-left: 8px; } </style> <div class="modal"> <div class="header"> <div class="title"><slot name="title"></slot></div> <div class="close">×</div> </div> <div class="content"><slot name="content"></slot></div> <div class="footer"><slot name="footer"></slot></div> </div> `; } get visible() { return this.hasAttribute('visible'); } set visible(val) { if (val) { this.setAttribute('visible', ''); } else { this.removeAttribute('visible'); } } attributeChangedCallback(name, oldValue, newValue) { if (name === 'visible') { if (this.visible) { document.body.style.overflow = 'hidden'; } else { document.body.style.overflow = ''; } } } connectedCallback() { this.shadowRoot.querySelector('.close').addEventListener('click', () => { this.visible = false; }); } } customElements.define('my-modal', MyModal);
在上面的代码中,我们定义了一个名为 MyModal 的类,继承自 HTMLElement 类。在构造函数中,我们通过 innerHTML 属性设置了 Shadow DOM 的内容,包括了一个样式和一个弹窗,弹窗中包含了三个 slot,用来接受插入的标题、内容和底部操作区域。
接着,我们定义了一个 visible 属性,通过 get 和 set 方法来获取和设置 visible 属性的值。在 attributeChangedCallback 回调函数中,我们监听 visible 属性的变化,并且根据新值修改弹窗的显示状态,并且禁止或允许页面滚动。在 connectedCallback 回调函数中,我们添加了一个点击关闭按钮的事件监听器。
接下来,我们可以通过 my-modal 元素来使用这个弹窗组件:
<my-modal visible> <div slot="title">Modal Title</div> <div slot="content">Modal Content</div> <div slot="footer"> <button class="button">Cancel</button> <button class="button">OK</button> </div> </my-modal>
在上面的代码中,我们定义了一个 my-modal 元素,并且通过 slot 插入了标题、内容和底部操作区域。通过设置 visible 属性为 true,可以让弹窗显示出来。
总结
Custom Elements 是 Web Components 规范中非常重要的一部分,它允许开发者创建自定义元素,并且可以通过 JavaScript 来定义它们的行为。Custom Elements 还提供了一些生命周期回调函数、属性和属性观察器等功能,可以帮助我们更加灵活地开发复用性高的 UI 组件。
以上就是本文对 Web Components 规范中的 Custom Elements 部分的解读与实践,希望对大家有所帮助。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/650e631a95b1f8cacd791c5b