Web Components是一组Web原生API的总称,允许开发人员创建可重用的组件。
我们认为Vue和Web Components大体上是互补的技术。Vue能很好的解析和创建自定义元素。不论是在将自定义元素正和岛已有的Vue应用中,还是使用Vue构建和分发自定义元素,你都能获得很好的支持。
Vue在Custom Elements Everywhere测试中获得了100%的完美分数。Vue应用程序中解析出的自定义元素大体上和元素HTML元素相同,但需要牢记以下几点:
默认情况下,Vue会优先尝试将一个非原生的HTML标签解析为一个注册的Vue组件,如果失败则会将其渲染为自定义元素。这种行为会导致在开发模式下的Vue发出‘failed to resolve component’的警告。如果你希望Vue能将某些确切的元素作为自定义元素处理并跳过组件解析,请指定compilerOptions.isCustomElement选项。
如果你正在构建步骤中使用Vue,则此选项需要通过构建配置传递,因为这是一个编译时选项。
//仅当使用浏览器内编译时有效
//如果你正在使用构建工具,请查看下方的配置示例
app.config.compilerOptions.isCustomElement = tag => tag.includes('-')
//vite.config.js
import vue from '@vitejs/plugin-vue'
export default {
plugins:[
vue({
template:{
compilerOptions:{
//将所有包含短横线的标签作为自定义元素处理
isCustomElement:tag => tag.includes('-')
}
}
})
]
}
//vue.config.js
module.exports = {
chainWebpack:config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(option => ({
...options,
compilerOptions:{
//将所有以ion-开头的标签作为自定义元素处理
isCustomElement:tag => tag.startsWith('ion-')
}
}))
}
}
由于DOM attribute只能是字符串,因此我们得将复杂数据作为DOM property传递给自定义元素。在自定义元素上配置porp时,Vue 3会自动使用in操作符检查是否存在DOM property,如果此键存在则会优先将值配置为一个DOM property。也就是说大多数情况下,如果自定义元素遵守推荐的最佳实践,则无需考虑这一点。
但是,在极少数情况下,数据必须作为DOM property传递,但自定义元素没有正确定义/反应property(导致in检查失败)。此时,可以使用.prop修饰符强制将一个v-bind绑定设置为一个DOM property:
<my-element :user.prop="{name:'jack'}"></my-element>
<!--等效的简写-->
<my-element .user="{name:'jack'}"></my-element>
自定义元素的一大好处就是它们可以与任何框架一起使用,甚至可以在没有框架的情况下使用。当你需要向使用不同前端技术栈的终端用户分发组件时,或者希望向最终应用程序隐藏其所用组件的实现细节时,使用自定义元素非常适合。
Vue支持使用defineCustomElement方法创建自定义元素,并且使用与Vue组件完全一致的API。该方法接受与defineComponent相同的参数,但是会返回一个扩展自HTMLElement的自定义元素构造函数:
<my-vue-element></my-vue-element>
import {defineCustomElement} from 'vue'
const MyVueElement = defineCustomElement({
//在此提供正常的Vue组件选项
props:{},
emits:{},
template:``,
//defineCustomElement独有特性:css会被注入到隐式根(shadow root)中
style:[]
})
//注册自定义元素
//注册完成后,此页面上的所有<my-vue-element>标签会被更新
customElements.define('my-vue-element',MyVueElement)
//你也可以编程式地实例化这个元素:(只能在注册后完成此操作)
document.body.appendChild(
new MyVueElement({
//initial props(optional)
})
)
props:{
selected:Boolean,
index:Number,
}
在组件中,selected会被转换为true,index会被转换为1
在自定义元素中,通过this.$emit或在setup中的emit发出的事件会被调度为原生CustomEvents。附加的事件参数(payload)会作为数组暴露在CustomEvent对象的detail property上。
在组件内部,可以像往常一样使用<slot/>渲染插槽。但是在解析最终生成的元素时,它只接受原生插槽语法
<my-element>
<div slot='named'>hello</div>
</my-element>
Provide/Inject API和组合式API中的Provide/Inject 在Vue定义的自定义元素之间可以正常工作。但是请注意这只适用于自定义元素之间,即Vue定义的自定义元素将无法注入非自定义元素的Vue组件提供的属性。
defineCustomElement也适用于Vue单文件组件(SFC),但是,在默认工具链配置下,生产构建时SFC内部的<style>会被提取并合并到单独的CSS文件中。当使用SFC作为自定义元素时,通常需要将<style>标签注入自定义元素的隐式根。
官方SFC工具支持以“自定义元素模式”导入SFC,以自定义元素模式加载的SFC将其<style>标签作为CSS字符串内联,并在组件的styles选项中暴露出来,然后会被defineCustomElement获取并在实例化时注入隐式根。
要选用此模式,只需使用.ce.vue作为文件拓展名即可:
import {defineCustomElement} from 'vue'
import Example from './Example.ce.vue'
console.log(Example.styles)//内联的CSS
//转换为自定义元素构造器
const ExampleElement = defineCustomElement(Example)
//注册
customElement.define('my-example',ExampleElement)
如果你希望指定应在自定义元素模式下导入的文件(例如将所有SFC视为自定义元素),你可以将customElement选项传递给对应的构造插件: