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); 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); } connectedCallback() { console .log("连接调用" ); } 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 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>