web组件的底层实现
一、HTML模版+影子DOM+自定义元素
-
HTML模板(
<template>
标签)- 作用:提供可复用的DOM片段,内容默认不渲染,通过JS动态插入。
- 特点:
- 模板内容在页面加载时存在但不可见,避免初始渲染开销。
- 支持动态克隆(
template.content.cloneNode(true)
)实现复用。
- 类比:类似“预制构件”,需要时按需组装。
-
影子DOM(Shadow DOM)
- 作用:封装组件内部DOM,避免全局样式/脚本污染,实现样式和作用域隔离。
- 关键点:
- 通过
attachShadow()
方法创建影子根节点(shadowRoot
)。 - 影子DOM内的样式不会泄漏到全局,外部样式也无法直接穿透(需通过
::part
或::theme
暴露接口)。
- 通过
- 类比:类似“黑盒子”,内部实现对外透明,仅通过接口交互。
-
自定义元素(Custom Elements)
- 作用:通过
class
继承HTMLElement
定义新标签,扩展浏览器原生元素能力。 - 生命周期:
constructor()
:初始化逻辑。connectedCallback()
:组件插入DOM时触发。disconnectedCallback()
:组件移除时触发。attributeChangedCallback()
:监听属性变化。
- 类比:类似“用户自定义控件”,可封装行为和样式。
- 作用:通过
二、三者结合实现web组件的示例:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><div>123</div><!-- 组件挂载会执行自定义元素的构造函数 --><my-component><div>插槽内容</div></my-component><template id="my-template"><slot></slot><div>HTML模版内容</div></template><script>class MyComponent extends HTMLElement {constructor() {super()console.log('执行了自定义元素的构造函数')//影子DOMthis.attachShadow({ mode: 'open' })//将模版内容添加到影子DOM中this.shadowRoot.appendChild(document.getElementById('my-template').content.cloneNode(true))}}//将组件和标签名关联起来customElements.define('my-component', MyComponent)</script>
</body>
</html>
效果图: