Custom Elements 和 Shadow DOM了解一下?
Why
在最近参与的一个项目中,有使用 Vue 创建 Custom Elements的代码,与传统的单 Vue 实例 render 到 DOM 节点有很大不同。所以有了本文。
Custom Elements
首先我们来看看怎么创建一个自定义元素,假如我们要实现一个抽屉元素:
class AppDrawerElement extends HTMLElement {...}
window.customElements.define('app-drawer', AppDrawerElement);
这里我们定义了一个AppDrawerElement
类,将它实例化就可以得到一个抽屉元素,正如我们创建一个 button 时,其实是一个HTMLButtonElement
的实例一样,在第二行代码中,我们将抽屉元素对应的HTML标签命名为'app-drawer';接下来我们就可以创建一个抽屉元素,并插入到 Body 中了
const drawer = document.createElement('drawer');
document.body.appendChild(drawer);
但是这样的 element 毫无用处,也不能显示隐藏, 我们希望它能显示隐藏,那么需要完善AppDrawerElement的实现,主要包含constructor
,connectedCallback
, disconnectedCallback
,attributeChangedCallback
等方法,他们的职责分别是:
实现上述方法后代码如下:
class AppDrawer extends HTMLElement {
constructor() {
super(); // always call super() first in the constructor.
}
static get observedAttributes() { // 哪些属性的变化可以触发 attributeChangedCallback
return ['open'];
}
get open() {
return this.hasAttribute('open');
}
set open(val) {
if (val) {
this.setAttribute('open', '');
} else {
this.removeAttribute('open');
}
this.toggleDrawer();
}
toggleDrawer() {
console.log(this.open);
if (this.open) {
this.children[0].style.cssText="display: block;"
} else {
this.children[0].style.cssText = 'display: none';
}
}
connectedCallback() {
// ...
this.innerHTML = '<div style="display:none">drawer</div>';
}
disconnectedCallback() {
// ...
}
attributeChangedCallback(attrName, oldVal, newVal) {
console.log(attrName, oldVal, newVal);
if (attrName === 'open') {
this.toggleDrawer();
}
// ...
}
}
这里要注意下observedAttributes这个静态属性,在这个属性中的 attribute 名才会触发 attributeChangedCallback,除此之外如 class,style 的变化不会触发回调函数。
虽然现在可以显示隐藏了,但是如何在元素中自定义 css 并且不影响全局呢,此外像 Vue 中的 slot 又改如何实现,这里就要用到 Shadow DOM了
Shadow DOM
Shadow DOM最重要的功能之一是实现了样式隔离,当我们在开发一个大型前端项目时,随着 CSS 样式越来越多,很可能会遇到样式冲突的问题,为了解决这个问题,社区给出了 CSS-modules,scoped css等方案,而 Shadow DOM Web 标准给出的方案。
在 Shadow DOM 中的元素不会受到外部样式的影响,而 Shadow DOM 中的样式也不会影响到外部元素。