持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
WebComponents简介
众所周知,复杂的程序可以拆解为一个一个小的简单的程序,这样就可以把复杂的架构简单化,我们可以通过开发一个一个的可视化组件,将组件的逻辑写入内部,并将简单组件组合形成一个复杂的项目,从而避免我们的项目变得过于复杂且混乱,而WebCOmponents便可以完成这个任务。WebComponents是一套可以用于创建重复的定制元素,并在web中进行复用的技术,这就Vue、React等框架的组件类似,但WebComponents是可以被原生的浏览器所支持的,并不需要runtime文件。现在也有一些微前端框架使用了
WebComponents技术组成
WebComponents技术进行实现,从而实现,样式隔离,技术栈无关等特点。WebComponent主要由一些三个部分组成:
- Custom Element: 主要用于定义一个自定义的HTML元素,并封装该自定义元素的行为,这就类似于JSP技术中的自定义标签
- Shadow DOM: 主要是用于创建一个内部的DOM元素,这个DOM对外界是不可见的,是一个独立的文档上下文,从而可以保证元素的私有化,而不必担心与其他文档的冲突
- CSS Scoping: 声明仅用于组件的Shadow DOM内的样式
- Template and slot: 主要用于HTML片段的复用,可以用于编写不在页面中展示的模板,然后作为自定义元素被复用
以上这些技术是实现WebComponents技术的一些原生的WebAPI支持,从而来帮助我们更好的构建组件化的应用
WebComponents实现步骤
- 创建一个类来定义自定义组件的功能
- 使用
CustomElementRegistry.define()来定义一个Custom Element元素,此处可以定义我们定义的组件的自定义元素的名称,以及指定该组件处理的类,我们使用上一步所创建的类 - 通常情况下,为了使得组件的私有化,我们会使用
Element.attachShadow()方法将一个shadow DOM附加到自定义元素上。然后我们向Shadow DOM内添加子元素,监听事件等,从而可以使得这些操作不会对外界造成影响。 - 对于一些复杂的元素,我们可以通过用
<template>定义可复用的标记模板,然后我们通常会调用templateElement.context.cloneNode(),将template内的DOM的副本添加到文档内,一些元素也可以通过<slot>对一些元素定义插槽,这个就类似于Vue中的slot - 然后我们可以像使用HTML原生元素一样,在HTML中使用我们的自定义元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<firstComponents id="first" img="./alt.jpg" text="测试文字"></firstComponents>
</body>
<script>
// 创建一个类
class FirstComponents extends HTMLElement {
constructor() {
super();
// 创建一个ShadowDOM
//此处为挂载shadowDOM的关键
const shadow = this.attachShadow({mode: 'open'});
//不可以挂载真实DOM,不符合HTML5标准要求
//const shadow = document.createElement('div');
//this.append(shadow)
//创建标签
const wrapper = document.createElement('div');
wrapper.setAttribute('class', 'wrapper');
const icon = document.createElement('span');
icon.setAttribute('class', 'icon');
icon.setAttribute('tabindex', 0);
const info = document.createElement('span');
info.setAttribute('class', 'info');
// Take attribute content and put it inside the info span
const text = this.getAttribute('text');
info.textContent = text;
// 插入图标
let imgUrl;
if(this.hasAttribute('img')) {
imgUrl = this.getAttribute('img');
} else {
imgUrl = './default.jpg';
}
const img = document.createElement('img');
img.src = imgUrl;
icon.appendChild(img);
// 创建CSS样式,并用于Shadow DOM,在Shodow DOM上的样式不会影响外部文档的样式。
const style = document.createElement('style');
style.textContent = `
.wrapper {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
margin: 50px;
}
.info {
font-size: 0.8rem;
width: 200px;
display: inline-block;
border: 1px solid black;
padding: 10px;
background: white;
border-radius: 10px;
opacity: 0;
transition: 0.6s all;
position: absolute;
bottom: 20px;
left: 10px;
z-index: 3;
}
img {
width:300px;
heigjt: 100%
}
.icon:hover + .info, .icon:focus + .info {
opacity: 1;
}
`;
// 将节点挂载到Shadow DOM
shadow.appendChild(style);
shadow.appendChild(wrapper);
wrapper.appendChild(icon);
wrapper.appendChild(info);
}
}
// 定义Custom element
customElements.define('firstComponents', FirstComponents);
</script>
</html>