How to build Web Components using Vue3? The following article will introduce you to the method of building Web Components with Vue3. I hope it will be helpful to you!
Sometimes I want to write a framework-independent component, but I don’t want to use native or Jquery to write it, and I also need to avoid style conflicts. It just feels right to use Web Components to do it. Quite appropriate. But now Web Components are still not flexible enough to use, and they are still inconvenient in many places. It would be great if they could be used in conjunction with MVVM. Angular has supported building components into Web Components long before. Vue3 3.2 finally supports building components into Web Components. I happened to want to reconstruct the comment plug-in recently, so I gave it a try.
vue provides a defineCustomElement method to convert the vue component into a custom function construct that extends to HTMLElement The function and usage are basically the same as the defineComponent parameter API. [Related recommendations: vuejs video tutorial]
import { defineCustomElement } from 'vue' const MyVueElement = defineCustomElement({ // 在此提供正常的 Vue 组件选项 props: {}, emits: {}, template: `...`, // defineCustomElement 独有特性: CSS 会被注入到隐式根 (shadow root) 中 styles: [`/* inlined css */`] }) // 注册 Web Components customElements.define('my-vue-element', MyVueElement)
If you need to use a single file, you need @vitejs/plugin-vue@^1.4.0 or vue -loader@^16.5.0 or higher version tool. If only some files need to be used, you can change the suffix to .ce.vue. If you need to build all filesWeb Components, you can @vitejs/plugin-vue@^1.4.0 or vue-loader@^16.5.0 The customElement configuration item is enabled. This way you no longer need to use the .ce.vue suffix name.
vue will map all the props to the properties of the custom element object, and also map the attributes on the custom element tags.
<com-demo></com-demo> props:{ type:String }
Because HTML's attribute can only be strings, except for basic types (Boolean, Number) Vue will help with type conversion during mapping, and other complex types need to be set to the DOM. on property.
In custom elements, via this.$emit or in setup Events emitted by emit will be dispatched as native CustomEvents. Additional event parameters (payload) are exposed as an array on the details property of the CustomEvent object.
When writing components, you can use it like vue, but you can only use native slot syntax, so scope slots are no longer supported.
When using nested sub-components, there is a pitfall in that the styles in the sub-components will not be extracted by default.
Parent component
<template> <div>{{ title }}</div> <childer></childer> </template> <script> import Childer from "./childer.vue" export default { components: { Childer }, data() { return { title: "父组件" } }, } </script> <style> .title { padding: 10px; background-color: #eee; font-weight: bold; } </style>
Subcomponent
<template> <div>{{ title }}</div> </template> <script> export default { data() { return { title: "子组件" } }, } </script> <style> .childer { padding: 10px; background-color: #222; color: #fff; font-weight: bold; } </style>
You can see that the style of the subcomponent is not inserted, but the style isolation identifier is generated data-v-5e87e937. I don’t know if Vue official will fix this bug in the future
If you look at the components, you can see that the styles of the sub-components have been extracted, so you only need to inject them yourself. .
Extract the sub-component style and insert it into the parent component, refer to this implementation
import ComDemo from '~/demo/index.vue' const deepStylesOf = ({ styles = [], components = {} }) => { const unique = array => [...new Set(array)]; return unique([...styles, ...Object.values(components).flatMap(deepStylesOf)]); } // 将子组件样式插入到父组件里 ComDemo.styles = deepStylesOf(ComDemo) !customElements.get('com-demo') && customElements.define('com-demo', defineCustomElement(ComDemo))
Perfectly solve the sub-component style problem
defineCustomElement By default, the built component will not attach methods to customElement. Looking at the Vue source code, there are only _def (constructor), _instance (component instance)). If you want to call a method in the component, dom._instance.proxy.fun(), it feels really inelegant.
Of course we hope that the methods exposed by our components can be directly removed from dom.fun() like ordinary dom. We have slightly expanded defineCustomElement.
import { VueElement, defineComponent } from 'vue' const defineCustomElement = (options, hydate) => { const Comp = defineComponent(options); class VueCustomElement extends VueElement { constructor(initialProps) { super(Comp, initialProps, hydate); if (Comp.methods) { Object.keys(Comp.methods).forEach(key => { // 将所有非下划线开头方法 绑定到 元素上 if(!/^_/.test(key)){ this[key] = function (...res) { if (this._instance) { // 将方法thi改为 组件实例的proxy return Comp.methods[key].call(this._instance.proxy, ...res) } else { throw new Error('未找到组件实例') } } } }) } } } VueCustomElement.def = Comp; return VueCustomElement; }
Generally speaking, there are still a lot of pitfalls. If you only need to build some relatively simple cross-frame plug-ins, you can also use this method to build Web Components. A good solution.
For more programming-related knowledge, please visit: Introduction to Programming! !
The above is the detailed content of Let's talk about how to use Vue3 to build Web Components. For more information, please follow other related articles on the PHP Chinese website!