Web Components 提供了基于原生支持的、对视图层的封装能力,可以让单个组件相关的 javaScript、css、html 模板运行在以 html 标签为界限的局部环境中,不会影响到全局,组件间也不会相互影响 。 再简单来说:就是提供了我们自定义标签的能力,并且提供了标签内完整的生命周期 。

Custom elements(自定义元素):JavaScript API,允许定义 custom elements 及其行为,然后可以在我们的用户界面中按照需要使用它们。

Shadow DOM(影子 DOM):JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,开发者可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。

HTML templates(HTML 模板):和元素使开发者可以编写与 HTML 结构类似的组件和样式。然后它们可以作为自定义元素结构的基础被多次重用。

一、web components 示例:

btn.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Btn extends HTMLElement {
constructor() {
super();
const shadowDom = this.attachShadow({ mode: "open" });
this.p = this.h("p");
this.p.innerText = "jude";
this.p.setAttribute(
"style",
"width:100px;height:100px;border:1px solid #999;background:yellowgreen"
);
shadowDom.appendChild(this.p);

// tempalte
this.template = this.h("template");
this.template.innerHTML = `
<style>
div{
width:50px;
height:50px;
background:green;
}
</style>
<div>
我是template,上面的样式会被隔离
</div>
`;
shadowDom.appendChild(this.template.content.cloneNode(true));
}
h(el) {
return document.createElement(el);
}
// 生命周期
// 当自定义元素第一次被连接到文档DOM时被调用
connectedCallback() {
console.log("连接调用");
}
// 当自定义元素与文档DOM断开连接时被调用
disconnectedCallback() {
console.log("断开调用");
}
// 当自定义元素被移动到新文档时被调用
adoptedCallback() {
console.log("移动时调用");
}
// 当自定义元素的第一个属性被增加、移除或者更改时被调用
attributeChangedCallback(attrName, oldVal, newVal) {
console.log("改变调用", attrName, oldVal, newVal);
}
}

window.customElements.define("yu-btn", Btn);

这里存在一个问题,如果使用 window.customElements.define(“btn”, Btn),会报错:Uncaught DOMException: Failed to execute ‘define’ on ‘CustomElementRegistry’: “btn” is not a valid custom element name,修改一下 name 即可

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!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>
<script src="./btn.js"></script>
</head>
<body>
<yu-btn></yu-btn>
</body>
</html>

二、vue 使用自定义组件

defineCustomElement

首先需要告知 vue 这是一个自定义组件,跳过组件检查

1
2
3
4
5
6
7
8
/*vite config ts 配置*/
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.includes("yu-"),
},
},
});

父组件:

这里需要注意,传递参数 如果是对象需要序列化 他是作用于 标签上的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<div>
<yu-btn :title="JSON.stringify(name)"></yu-btn>
</div>
</template>

<script setup lang="ts">
import { ref, reactive, defineCustomElement } from "vue";
//自定义元素模式 要开启这个模式,只需要将你的组件文件以 .ce.vue 结尾即可
import customVueVue from "./components/custom-vue.ce.vue";
const Btn = defineCustomElement(customVueVue);
customElements.define("yu-btn", Btn);

const name = ref({ a: 1 });
</script>

<style scoped lang="less"></style>

子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div>title: {{ title }}</div>
</template>

<script setup lang="ts">
import { ref, reactive } from "vue";

defineProps<{
title: string;
}>();
</script>

<style scoped lang="less"></style>