Web Component 是啥
Web Component 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 web 应用中使用它们。 —
Web Component | MDN
在 Vue React Angular 下开发,组件化已经是必备的内容了。还有 Antd ElementUi 等 ui 组件库让我们开发也是越来越简单了,但是是否能不用框架进行组件化开发呢?那 web Component 就是解决方案。
Web Component 是 W3C 标准支持的组件化方案,它可以让我们可以编写可复用的组件,它并不是一项单一的技术,而是由三大部分组成
- Template: Template 生成 DOM
- Shadow DOM:Shadow DOM 来隔离CSS样式
- Custom Elements: Custom Elements 来自定义元素,继承自 HTMLElement ,HTMLElement 是 DOM API 里面的一个类,继承该类就有了html 的常见属性和 API
使用 Web Component
开始实现一个简单的 Web Component 组件体验下,一个可以简单计数的 Counter 组件。
- 定义模板
<template id="counter-template">
<style>
/* ... */
</style>
<div class="counter">
<button id="add">+</button>
<span id="count">0</span>
<button id="sub">-</button>
</div>
<slot>
<p>web counter</p>
</slot>
</template>
<template id="counter-template">
<style>
/* ... */
</style>
<div class="counter">
<button id="add">+</button>
<span id="count">0</span>
<button id="sub">-</button>
</div>
<slot>
<p>web counter</p>
</slot>
</template>
- 编写组件逻辑
class CounterComponent extends HTMLElement {
addEl: null | HTMLElement
subEl: null | HTMLElement
countEl: null | HTMLElement
constructor() {
super();
// 深度克隆一份template
const template = document.getElementById("counter-template") as HTMLTemplateElement;
const dom = template.content.cloneNode(true);
// open: 可以从 js 外部访问 shadow root 节点
// closed: 拒绝从 js 外部访问 shadow root 节点
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(dom);
this.addEl = shadowRoot.getElementById('add');
this.subEl = shadowRoot.getElementById('sub');
this.countEl = shadowRoot.getElementById('count');
this.render();
}
// 获取count
get count() {
return this.getAttribute("count") ? Number(this.getAttribute("count")) : 0;
}
// 设置count值
set count(count) {
this.setAttribute("count", String(count));
this.render();
}
connectedCallback() {
// 当 custom element首次被插入 DOM 时
this.addEl?.addEventListener("click", () => {
this.count = this.count + 1;
});
this.subEl?.addEventListener("click", () => {
this.count = this.count - 1;
});
}
disconnectedCallback() {
// 当 custom element 从 DOM 中删除时
console.log('web-conter disconnectedCallback');
}
render() {
if (this.countEl) {
this.countEl.innerText = String(this.count)
}
}
}
class CounterComponent extends HTMLElement {
addEl: null | HTMLElement
subEl: null | HTMLElement
countEl: null | HTMLElement
constructor() {
super();
// 深度克隆一份template
const template = document.getElementById("counter-template") as HTMLTemplateElement;
const dom = template.content.cloneNode(true);
// open: 可以从 js 外部访问 shadow root 节点
// closed: 拒绝从 js 外部访问 shadow root 节点
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(dom);
this.addEl = shadowRoot.getElementById('add');
this.subEl = shadowRoot.getElementById('sub');
this.countEl = shadowRoot.getElementById('count');
this.render();
}
// 获取count
get count() {
return this.getAttribute("count") ? Number(this.getAttribute("count")) : 0;
}
// 设置count值
set count(count) {
this.setAttribute("count", String(count));
this.render();
}
connectedCallback() {
// 当 custom element首次被插入 DOM 时
this.addEl?.addEventListener("click", () => {
this.count = this.count + 1;
});
this.subEl?.addEventListener("click", () => {
this.count = this.count - 1;
});
}
disconnectedCallback() {
// 当 custom element 从 DOM 中删除时
console.log('web-conter disconnectedCallback');
}
render() {
if (this.countEl) {
this.countEl.innerText = String(this.count)
}
}
}
- 注册使用
// 注册
window.customElements.define("web-conter", CounterComponent);
// 注册
window.customElements.define("web-conter", CounterComponent);
<!-- 使用 -->
<web-counter count="10">
<p slot="desc">slot example counter</p>
</web-counter>
<!-- 使用 -->
<web-counter count="10">
<p slot="desc">slot example counter</p>
</web-counter>
- example
0
web counter
slot example counter
最后
已经有 Vue、React 了 Web Component 那它的有啥好处呢?
- 标准支持 w3c 标准,主流浏览器支持
- 技术栈无关 可以在任何框架使用
- 开箱即用 不需要引入其他依赖
- 天然的样式隔离
当然目前也存在一些问题
- 兼容性问题
customElements - 书写较为繁琐,需要自己进行原生dom操作
- 需要自己进行状态管理,处理重新渲染逻辑
想要在高度工程化的今天直接使用的话,开发体验并十分不友好还是需要更多的生态。