Web Components 是一种用于创建可重用组件的技术,而 Custom Elements 则是其中的一个重要标准。通过 Custom Elements,我们可以创建出自定义的 HTML 元素,并且将它们与 JavaScript 类相结合。
在实践 Custom Elements 过程中,继承和复合是两种常见的组件设计方式。本文将介绍 Custom Elements 下的继承和复合组件的实践方法,并提供一些实用的示例代码。
继承组件
继承组件是一种创建 Custom Elements 的方法,它可以让我们在不需要编写重复代码的情况下,创建出一系列具有共同特性的组件。
如下所示,我们创建了一个基础组件 my-element
,它具有类似于 div
标签的基本样式,并且可以根据传入的 color
属性控制背景颜色。
// javascriptcn.com 代码示例 <!-- my-element.html --> <template> <style> :host { display: block; width: 200px; height: 200px; background-color: var(--background-color, #fff); } </style> <slot></slot> </template> <script> class MyElement extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild( document.importNode(template.content, true) ); } static get observedAttributes() { return ['color']; } attributeChangedCallback(name, oldValue, newValue) { switch (name) { case 'color': this.shadowRoot.host.style.setProperty( '--background-color', newValue ); break; } } } customElements.define('my-element', MyElement); </script>
现在我们想要创建一个基于 my-element
的 card-element
,它会继承 my-element
的基础样式,并根据传入的 title
和 description
属性展示卡片信息。我们可以通过继承 MyElement
类来实现:
// javascriptcn.com 代码示例 <!-- card-element.html --> <link rel="import" href="my-element.html"> <template> <style> :host { display: block; border: 1px solid #ccc; padding: 16px; } h1 { margin-top: 0; } p { margin-bottom: 0; } </style> <slot name="title"></slot> <slot name="description"></slot> </template> <script> class CardElement extends MyElement { constructor() { super(); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild( document.importNode(template.content, true) ); } static get observedAttributes() { return ['title', 'description']; } attributeChangedCallback(name, oldValue, newValue) { switch (name) { case 'title': this.shadowRoot.querySelector('slot[name="title"]').textContent = newValue; break; case 'description': this.shadowRoot.querySelector('slot[name="description"]').textContent = newValue; break; } } } customElements.define('card-element', CardElement); </script>
在上面的代码中,我们重新定义了 CardElement
类,并继承了 MyElement
类。CardElement
类可以访问到 MyElement
类的方法和属性,比如 observedAttributes
和 attributeChangedCallback
。
通过继承 MyElement
类,我们非常方便地创建了一个新的组件,它具有与 my-element
相同的样式,并且可以根据传入的 title
和 description
展示卡片信息。因此,在组件开发过程中,我们应该尽可能地使用继承来减少重复代码的编写。
复合组件
复合组件是另一种创建 Custom Elements 的方法,它可以让我们将多个组件组合成一个功能完整的组件。
如下所示,我们创建了一个 tabs-element
,它由多个 tab-element
组成,并且可以通过传入的 active
属性控制显示哪一个 tab-element
。
// javascriptcn.com 代码示例 <!-- tabs-element.html --> <template> <style> :host { display: block; } .tab { display: none; } .tab.active { display: block; } .nav { display: flex; justify-content: space-between; align-items: center; padding: 8px; background-color: #f5f5f5; border: 1px solid #ccc; border-radius: 4px 4px 0 0; } .nav button { border: none; background: none; cursor: pointer; font-weight: bold; } </style> <div class="nav"> <button data-index="0">Tab 1</button> <button data-index="1">Tab 2</button> <button data-index="2">Tab 3</button> </div> <slot></slot> </template> <script> class TabsElement extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild( document.importNode(template.content, true) ); this.buttons = Array.from(this.shadowRoot.querySelectorAll('button')); this.tabs = Array.from(this.children); this.selected = 0; this.selectedIndex = -1; this.update(); } connectedCallback() { this.onButtonClick = this.onButtonClick.bind(this); this.buttons.forEach(button => button.addEventListener('click', this.onButtonClick)); } disconnectedCallback() { this.buttons.forEach(button => button.removeEventListener('click', this.onButtonClick)); } update() { this.buttons.forEach((button, index) => { button.classList.toggle('active', index === this.selected); }); this.tabs.forEach((tab, index) => { tab.classList.toggle('active', index === this.selected); }); } onButtonClick(event) { const index = parseInt(event.target.getAttribute('data-index'), 10); if (this.selected === index) { return; } this.selected = index; this.update(); } } customElements.define('tabs-element', TabsElement); </script>
在 tabs-element
组件中,我们定义了一个包含多个 tab-element
的 slot
,并通过 Array.from(this.children)
来获取到所有的 tab-element
子元素。
我们还通过添加 button
元素,并且为它们添加了一个 data-index
属性来控制选中哪一个 tab-element
。通过 update
方法,我们可以根据传入的 active
属性来控制显示哪一个 tab-element
。
代码如下所示:
// javascriptcn.com 代码示例 <!-- tab-element.html --> <template> <div class="tab"> <slot></slot> </div> </template> <script> class TabElement extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild( document.importNode(template.content, true) ); } } customElements.define('tab-element', TabElement); </script>
在上面的代码中,我们定义了一个 tab-element
组件,它包含了一些需要展示的内容,并通过 slot
元素来接受传入的内容。
通过使用复合组件的方式,我们可以将多个不同的组件组合成一个复杂的组件,并且可以根据需要随意进行扩展和定制。复合组件的优势在于它可以让组件的开发过程变得非常灵活和高效。
总结
在本文中,我们介绍了 Custom Elements 下的继承和复合组件实践方法,并提供了一些实用的示例代码。在组件的开发过程中,我们应该尽可能地使用继承来减少重复代码的编写,并且通过使用复合组件的方式来组合多个不同的组件。这些技术可以让我们创建出更加优雅和高效的 Web 组件,并且对于组件的复用和维护都具有非常重要的作用。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/654b64d77d4982a6eb5402d8