Web Components 是一组 web 原生 API 的统称,允许开发者创建可复用的自定义元素 (custom elements)。
创建自定义元素 defineCustomElement
Vue 提供了一个和定义 Vue 组件几乎完全一致的 defineCustomElement 方法来支持创建自定义元素。这个方法接收的参数和 defineComponent 完全相同。但它会返回一个继承自 HTMLElement 的自定义元素构造器:
<template>
<test-element :info="info" :obj.prop="obj"><div>1</div></test-element>
</template>
<script lang="ts" setup>
import { defineCustomElement, ref } from 'vue';
const info = ref('ddd');
const obj = ref({
name: 'name',
});
const testElement = defineCustomElement({
props: {
info: String,
obj: Object,
},
emits: {},
template: `<div class="test">{{ info }} - {{ obj.name }}<slot/></div>`,
styles: [
`.test { color: red; }`,
'@import "http://localhost:6666/src/view/home/index.css";',
],
});
customElements.define('test-element', testElement);
</script>
默认情况下,Vue 会将任何非原生的 HTML 标签优先当作 Vue 组件处理,而将“渲染一个自定义元素”作为后备选项。要让 Vue 知晓特定元素应该被视为自定义元素并跳过组件解析
vite配置
// vite.config.js
import vue from '@vitejs/plugin-vue'
export default {
plugins: [
vue({
template: {
compilerOptions: {
// 将所有带短横线的标签名都视为自定义元素
isCustomElement: (tag) => tag.includes('test-')
}
}
})
]
}
在vue这个构建中不支持运行时编译 需要配置你的 bundler 别名 vue: vue/dist/vue.esm-bundler.js
resolve: {
alias: {
'vue': 'vue/dist/vue.esm-bundler.js',
'@': resolve(__dirname, 'src')
}
}
props
所有使用props选项声明的props都会作为属性定义在改自定义元素上
事件
通过 this.$emit 或者 setup 中的 emit 触发的事件都会通过以 CustomEvents 的形式从自定义元素上派发。额外的事件参数 (payload) 将会被暴露为 CustomEvent 对象上的一个 detail 数组。
插槽
在一个组件中,插槽将会照常使用 <slot/> 渲染。然而,当使用最终的元素时,它只接受原生插槽的语法:
-
不支持作用域插槽。
-
当传递具名插槽时,应使用
slotattribute 而不是v-slot指令:
<test-element>
<div slot="named">hello</div>
</test-element>
将 SFC 编译为自定义元素
defineCustomElement 也可以搭配 Vue 单文件组件 (SFC) 使用
要选用此模式,只需使用 .ce.vue 作为文件拓展名即可。
// item.ce.vue
<template>
<h3> custom web components </h3>
<ul>
<li v-for="item in list" :key="item.name">
姓名:{{ item.name }} - 年龄:{{ item.age }}
</li>
</ul>
</template>
<script lang="ts" setup>
import { defineProps } from 'vue';
defineProps({test
list: {
type: Array,
default: () => [
{ name: 'y', age: 33 },
{ name: 'tom', age: 66 },
],
},
});
</script>
<style scoped>
li {
line-height: 36px;
}
</style>
import iconListCe from './item.ce.vue';
const IconList = defineCustomElement(iconListCe);
customElements.define('icon-list', IconList);