HTML Web Components实战:自定义元素开发完整指南
一、Web Components核心概念
Web Components是一套浏览器原生组件化技术,包含三个主要技术:
- Custom Elements:定义自定义HTML元素
- Shadow DOM:封装组件内部DOM结构
- HTML Templates:声明可复用的HTML模板
// 自定义元素示例
class HelloWorld extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: ‘open’});
this.shadowRoot.innerHTML = `
p { color: blue; }
Hello, Web Components!
`;
}
}
customElements.define(‘hello-world’, HelloWorld);
二、自定义元素开发实战
// 1. 定义自定义元素类
class UserCard extends HTMLElement {
constructor() {
super();
// 2. 创建Shadow DOM
this.attachShadow({mode: 'open'});
// 3. 定义模板
const template = document.createElement('template');
template.innerHTML = `
.card {
border: 1px solid #ccc;
padding: 16px;
display: flex;
align-items: center;
}
img {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 16px;
}
`;
// 4. 克隆模板并挂载
this.shadowRoot.appendChild(template.content.cloneNode(true));
}
// 5. 定义可观察属性
static get observedAttributes() {
return ['name', 'avatar', 'title'];
}
// 6. 属性变化回调
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'name') {
this.shadowRoot.querySelector('h3').textContent = newValue;
} else if (name === 'title') {
this.shadowRoot.querySelector('p').textContent = newValue;
} else if (name === 'avatar') {
this.shadowRoot.querySelector('img').src = newValue;
}
}
}
// 7. 注册自定义元素
customElements.define('user-card', UserCard);
三、生命周期方法详解
方法 | 调用时机 |
---|---|
constructor() | 元素创建时调用 |
connectedCallback() | 元素插入DOM时 |
disconnectedCallback() | 元素从DOM移除时 |
attributeChangedCallback() | 观察属性变化时 |
adoptedCallback() | 元素被移动到新文档时 |
四、与主流框架对比
Web Components优势
- 框架无关:原生支持,无需依赖
- 高性能:浏览器原生实现
- 高兼容:可与其他框架共存
- 真隔离:Shadow DOM实现样式隔离
适用场景
- 跨框架UI组件库
- 微前端架构
- 需要长期维护的基础组件
- 对性能要求高的场景
五、实际案例:可复用弹窗组件
class ModalDialog extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = `
.modal { /* 样式省略 */ }
.modal.open { display: block; }
`;
this.modal = shadow.querySelector('.modal');
}
static get observedAttributes() {
return ['open'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'open') {
this.modal.classList.toggle('open', newValue !== null);
}
}
get open() {
return this.hasAttribute('open');
}
set open(isOpen) {
if (isOpen) {
this.setAttribute('open', '');
} else {
this.removeAttribute('open');
}
}
}
customElements.define('modal-dialog', ModalDialog);
标题