Web Components 是一种新的 Web 技术,它提供了一种自定义 HTML 元素并封装其行为的方式。通过创建自定义元素,开发者能够在 Web 应用程序中使用组件库的方式来组织和重用代码,提高代码的可维护性和可重用性。在本文中,我们将介绍如何用 Web Components 开发一个全局组件库。
什么是 Web Components?
Web Components 是由 W3C 提出的一套技术规范,它包括了四个部分:
- Custom Elements:允许开发者创建自定义 HTML 元素。
- Shadow DOM:允许开发者将样式和行为封装在自定义 HTML 元素内部。
- HTML Templates:允许开发者定义可重用的 HTML 模板。
- HTML Imports:允许开发者以 HTML 形式导入其他 HTML 文件。
使用 Web Components 技术,开发者可以自定义 HTML 元素,并通过封装自己的样式和行为,将这些元素作为真正的组件来使用。
如何创建一个 Web Component?
Web Component 由 Custom Elements 和 Shadow DOM 两部分组成,Custom Elements 允许开发者创建自定义 HTML 元素,Shadow DOM 允许开发者将元素的外部样式和行为与内部样式和行为隔离开来。下面是一个简单的 Web Component 示例代码:
// www.javascriptcn.com code example
<!DOCTYPE html>
<html>
<head>
<title>Hello World Component</title>
<script>
class HelloWorld extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const div = document.createElement('div');
div.innerHTML = 'Hello World';
shadow.appendChild(div);
}
}
customElements.define('hello-world', HelloWorld);
</script>
</head>
<body>
<hello-world></hello-world>
</body>
</html>上面的代码片段定义了一个名为 HelloWorld 的 Web Component,它是通过一个继承自 HTMLElement 的 JavaScript 类来定义的。在 HelloWorld 类的构造函数中,我们调用了父类的构造函数,并创建了一个 Shadow DOM。之后,我们创建一个 div 元素,并将其内容设置为 "Hello World",最后将该 div 元素添加到 Shadow DOM 中。
在页面的 body 中,我们使用了 <hello-world> 标签来调用 HelloWorld 组件,并渲染出了 "Hello World"。
如何制作一个全局组件库?
要制作一个全局组件库,我们需要将多个 Web Components 组合在一起,并以可复用的方式将其导出。下面是一种可能的实现方式:
- 将所有 Web Components 放在一个单独的文件夹中,例如 component-lib。
- 为每个组件创建一个子文件夹,并在其中定义该组件的 Custom Element 和 Shadow DOM。
- 在每个组件的 CSS 中使用 :host 选择器来定义样式规则。
- 如果组件之间需要共享样式和函数,可以创建一个单独的文件夹,例如 utils,并将可复用的 stylesheet 和 JavaScript 文件放在其中。
- 在 utils 文件夹中,定义全局变量或函数,以便组件可以访问共享的代码。
- 导出组件库:在 component-lib 文件夹中创建一个 entry.js 文件,并将所有组件导入到该文件中,最后将组件库导出为一个单一的对象。
下面是一个组件库示例,在该示例中,我们创建了两个组件,分别是 MY-ALERT 和 MY-BUTTON。
MY-ALERT 组件
// www.javascriptcn.com code example
<!DOCTYPE html>
<html>
<head>
<title>MY-ALERT</title>
<link rel="stylesheet" href="../utils/utils.css">
<style>
:host {
display: block;
background-color: var(--backgroundColor, #f8d7da);
color: var(--color, #721c24);
border: 1px solid currentColor;
padding: 1rem;
}
:host(.success) {
background-color: var(--backgroundColor, #d4edda);
color: var(--color, #155724);
border-color: currentColor;
}
:host(.warning) {
background-color: var(--backgroundColor, #fff3cd);
color: var(--color, #856404);
border-color: currentColor;
}
:host(.info) {
background-color: var(--backgroundColor, #d1ecf1);
color: var(--color, #0c5460);
border-color: currentColor;
}
</style>
<script src="../utils/utils.js"></script>
</head>
<body>
<slot></slot>
<button onclick="onClose()">close</button>
<script>
class MyAlert extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const template = document.querySelector('#my-alert-template');
const content = template.content.cloneNode(true);
shadow.appendChild(content);
this.classList.add(getAttribute('type'));
this._closeButton = shadow.querySelector('button');
this._closeButton.addEventListener('click', this._onClose.bind(this));
}
_onClose() {
const closeEvent = new Event('close');
this.dispatchEvent(closeEvent);
}
}
customElements.define('my-alert', MyAlert);
</script>
<template id="my-alert-template">
<style>
div {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
<div>
<slot></slot>
<button>close</button>
</div>
</template>
</body>
</html>上面的代码片段定义了一个名为 MY-ALERT 的 Web Component,它是一个自定义的警告框,可以在页面中显示警告信息。通过使用 :host 选择器,我们可以在组件中设置样式,并允许用户通过使用 --backgroundColor 和 --color 变量来自定义样式。在组件的 JavaScript 中,我们监听了点击事件,并在点击事件触发时,将一个名为 "close" 的自定义事件派发到组件的父元素上。
MY-BUTTON 组件
// www.javascriptcn.com code example
<!DOCTYPE html>
<html>
<head>
<title>MY-BUTTON</title>
<link rel="stylesheet" href="../utils/utils.css">
<style>
:host {
display: inline-block;
background-color: var(--backgroundColor, #fff);
color: var(--color, #212529);
border: 1px solid currentColor;
padding: 0.75rem 1.5rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
text-decoration: none;
transition: all 0.15s ease-in-out;
}
:host(:hover) {
background-color: var(--hoverBackgroundColor, #f8f9fa);
color: var(--hoverColor, #212529);
border-color: currentColor;
}
:host(:focus) {
outline: none;
box-shadow: 0 0 0 0.2rem var(--focusBoxShadowColor, rgba(0, 123, 255, 0.25));
}
:host(:disabled) {
opacity: 0.65;
pointer-events: none;
}
</style>
<script src="../utils/utils.js"></script>
</head>
<body>
<button disabled><slot></slot></button>
<script>
class MyButton extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const template = document.querySelector('#my-button-template');
const content = template.content.cloneNode(true);
shadow.appendChild(content);
const button = shadow.querySelector('button');
button.addEventListener('click', this._onClick.bind(this));
}
_onClick() {
const clickEvent = new Event('click');
this.dispatchEvent(clickEvent);
}
static get observedAttributes() {
return ['disabled'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
if (newValue) {
this.shadowRoot.querySelector('button').setAttribute(name, newValue);
} else {
this.shadowRoot.querySelector('button').removeAttribute(name);
}
}
}
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(value) {
if (value) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
}
}
customElements.define('my-button', MyButton);
</script>
<template id="my-button-template">
<style>
button {
background-color: var(--backgroundColor, #fff);
color: var(--color, #212529);
border: 1px solid currentColor;
padding: 0.75rem 1.5rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
text-decoration: none;
transition: all 0.15s ease-in-out;
}
button:hover {
background-color: var(--hoverBackgroundColor, #f8f9fa);
color: var(--hoverColor, #212529);
border-color: currentColor;
}
button:focus {
outline: none;
box-shadow: 0 0 0 0.2rem var(--focusBoxShadowColor, rgba(0, 123, 255, 0.25));
}
button:disabled {
opacity: 0.65;
pointer-events: none;
}
</style>
<button><slot></slot></button>
</template>
</body>
</html>上面的代码片段定义了一个名为 MY-BUTTON 的 Web Component,它是一个自定义的按钮,可以在页面中用于用户交互。与 MY-ALERT 组件类似,我们在组件的 CSS 中使用 :host 选择器来设置样式,并允许用户通过使用 --backgroundColor 和 --color 变量来自定义样式。在组件的 JavaScript 中,我们监听了点击事件,并在点击事件触发时,将一个名为 "click" 的自定义事件派发到组件的父元素上。此外,我们还实现了 disabled 属性,并通过观察该属性的变化,来更新按钮的状态。
组件库导出
为了导出组件库,我们可以在 component-lib 文件夹中创建一个 entry.js 文件,并将所有组件导入到该文件中,最后将组件库导出为一个单一的对象:
// www.javascriptcn.com code example
import './my-alert/my-alert.js';
import './my-button/my-button.js';
const componentLib = {
MyAlert: customElements.get('my-alert'),
MyButton: customElements.get('my-button')
};
export default componentLib;上面的代码片段导入了 MY-ALERT 和 MY-BUTTON 两个 Web Component,并将它们导出为一个名为 componentLib 的对象。
如何使用组件库?
在使用组件库时,我们只需要将组件库的 entry.js 文件导入到我们的代码中,并开始使用其中定义的组件。下面是一个使用组件库的代码示例:
// www.javascriptcn.com code example
<!DOCTYPE html>
<html>
<head>
<title>Component Library Example</title>
<script type="module" src="./component-lib/entry.js"></script>
</head>
<body>
<my-alert class="success">This is a success message!</my-alert>
<my-alert class="warning">This is a warning message!</my-alert>
<my-alert class="info">This is an info message!</my-alert>
<my-button>Click Me!</my-button>
<script>
const button = document.querySelector('my-button');
button.addEventListener('click', () => {
console.log('Button Clicked!');
});
</script>
</body>
</html>在上面的代码中,我们将组件库的 entry.js 文件导入到代码中,并开始使用其中定义的 MY-ALERT 和 MY-BUTTON 组件。此外,我们还在 MY-BUTTON 组件上添加了一个点击事件监听器,当用户点击按钮时,会在控制台上输出 "Button Clicked!"。
结语
在本文中,我们介绍了 Web Components 技术,并详细介绍了如何使用 Web Components 开发一个全局组件库。通过这种方式,我们可以将组件库的代码组织为自包含的、可复用的、易于维护的代码块,从而提高我们的代码质量和开发效率。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d67afba941bf7134c406a6