Web Components
概念
Web Components提供了基于原生支持的、对视图层的封装能力。
它可以让单个组件相关的javaScript、css、html模板运行在以html标签为界限的局部环境中,不会影响到全局,组件间也不会相互影响。
就是提供了原生JS实现自定义标签的能力,并且提供了标签内完整的生命周期 。
- Custom elements(自定义元素):JavaScript API,允许定义custom elements及其行为,然后按需使用。
- Shadow DOM(影子DOM):JavaScript API,用于将封装的”影子“DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
- HTML templates(HTML模板):和元素使开发者可以编写与HTML结构类似的组件和样式。然后它们可以作为自定义元素结构的基础被多次重用。
使用
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 52 53 54 55 56 57 58
| class Btn extends HTMLElement { constructor() { super()
const shadowDom = this.attachShadow({ mode: "open" })
this.template = this.h("template") this.template.innerHTML = ` <style> div { width:200px; height:200px; border:1px solid black; } </style> <div>oh my god</div> ` shadowDom.appendChild(this.template.content.cloneNode(true)) }
h(el) { return document.createElement(el) }
connectedCallback() { console.log('connectedCallback') } disconnectedCallback() { console.log('disconnectedCallback') } adoptedCallback() { console.log('adoptedCallback') } attributeChangedCallback() { console.log('attributeChangedCallback') } }
window.customElements.define("my-btn", Btn)
|
index.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!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> <my-btn></my-btn> <my-btn></my-btn> <div>草</div> </body>
</html>
|
页面效果:
Vue+Web Components
在vite.config.ts中配置:
1 2 3 4 5 6 7 8 9 10 11 12
| export default defineConfig({ plugins: [ vue({ template: { compilerOptions: { isCustomElement:(tag)=>tag.includes("my-btn") } } }), ], })
|
custom-vue.ce.vue:
后缀名为.ce.vue的文件才能被识别为Web Components。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <button>OH MY GOD</button> </div> </template>
<script setup lang="ts"> defineProps<{ obj:any }>() </script>
<style scoped lang="scss"> button { width: 100px; height: 100px; } </style>
|
App.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div> <my-btn :obj="JSON.stringify(obj)"></my-btn> </div> </template>
<script setup lang="ts"> import { defineCustomElement } from "vue" import customVueCeVue from "./components/custom-vue.ce.vue"
const Btn = defineCustomElement(customVueCeVue) window.customElements.define("my-btn",Btn)
// 不能直接传参,因为参数内容会被映射到标签上 // 于是变成了<my-btn :obj="[object Object]"></my-btn> // 如果要传引用类型,只能使用stringify const obj = {name:"yajue"} </script>
|