如何使用 vue3 构建 web components?下面本篇文章给大家介绍一下用 vue3 构建 web components的方法,希望对大家有所帮助!
有时候想写一个无关框架组件,又不想用原生或者 jquery 那套去写,而且还要避免样式冲突,用 web components 去做刚觉就挺合适的。但是现在 web components 使用起来还是不够灵活,很多地方还是不太方便的,如果能和 mvvm 搭配使用就好了。早在之前 angular 就支持将组件构建成 web components,vue3 3.2+ 开始终于支持将组建构建成 web components 了。正好最近想重构下评论插件,于是上手试了试。
构建 web componentsvue 提供了一个 definecustomelement 方法,用来将 vue 组件转换成一个扩展至htmlelement的自定义函数构造函数,使用方式和 definecomponent 参数api基本保持一致。【相关推荐:vuejs视频教程】
import { definecustomelement } from 'vue' const myvueelement = definecustomelement({ // 在此提供正常的 vue 组件选项 props: {}, emits: {}, template: `...`, // definecustomelement 独有特性: css 会被注入到隐式根 (shadow root) 中 styles: [`/* inlined css */`]})// 注册 web componentscustomelements.define('my-vue-element', myvueelement)
如果需要使用单文件,需要 @vitejs/plugin-vue@^1.4.0 或 vue-loader@^16.5.0 或更高版本工具。如果只是部分文件需要使用,可以将后缀改为 .ce.vue 。若果需要将所有文件都构建 web components 可以将 @vitejs/plugin-vue@^1.4.0 或 vue-loader@^16.5.0 的 customelement 配置项开启。这样不需要再使用 .ce.vue 后缀名了。
属性vue 会把所有的的 props 自定义元素的对象的 property 上,也会将自定义元素标签上的 attribute 做一个映射。
<com-demo type="a"></com-demo>props:{ type:string}
因为 html 的 attribute 的只能是字符串,除了基础类型(boolean、number) vue 在映射时会帮忙做类型转换,其他复杂类型则需要设置到 dom property 上。
事件在自定义元素中,通过 this.$emit 或在 setup 中的 emit 发出的事件会被调度为原生 customevents。附加的事件参数 (payload) 会作为数组暴露在 customevent 对象的 details property 上。
插槽编写组件时,可以想 vue 一样,但是使用时只能原生的插槽语法,所以也不在支持作用域插槽。
子组件样式问题使用子组件嵌套的时,有个坑的地方就是默认不会将子组件里的样式抽离出来。
父组件
<template> <div>{{ title }}</div> <childer /></template><script>import childer from ./childer.vueexport default { components: { childer }, data() { return { title: 父组件 } },}</script><style scoped>.title { padding: 10px; background-color: #eee; font-weight: bold;}</style>
子组件
<template> <div>{{ title }}</div></template><script>export default { data() { return { title: 子组件 } },}</script><style scoped>.childer { padding: 10px; background-color: #222; color: #fff; font-weight: bold;}</style>
可以看到子组件的样式没有插入进去,但是样式隔离的标识是有生成的 data-v-5e87e937。不知道vue官方后续会不会修复这个bug
查看组件是可以看到,子组件的样式是有被抽离出来的,这样就只需要自己注入进去了。
将子组件样式抽离插入到父组件里,参考这个的实现
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))
完美解决子组件样式问题
方法definecustomelement 构建的组件默认是不会将方法挂到 customelement 上的,看 vue 源码中,只有 _def(构造函数),_instance(组件实例))。如果想调用组件内的方法,dom._instance.proxy.fun(),感觉实在不太优雅。
我们当然希望我们组件暴露的方法能像普通dom那样直接 dom.fun() 去掉用,我们对 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;}
总结总体来说坑还是有不少的,如果仅仅需要构建一些比较简单跨框架插件,使用这种方式来构建 web components 也是一种不错的方案。
更多编程相关知识,请访问:编程入门!!
以上就是聊聊怎么用vue3构建web components的详细内容。