Vue与Web Components

房泉
2023-12-01

Vue与Web Components

Web Components是一组Web原生API的总称,允许开发人员创建可重用的组件。
我们认为Vue和Web Components大体上是互补的技术。Vue能很好的解析和创建自定义元素。不论是在将自定义元素正和岛已有的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配置示例

//vite.config.js
import vue from '@vitejs/plugin-vue'
export default {
	plugins:[
		vue({
			template:{
				compilerOptions:{
					//将所有包含短横线的标签作为自定义元素处理
					isCustomElement:tag => tag.includes('-')
				}
			}
		})
	]
}

Vue CLI配置示例

//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 Property

由于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支持使用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)
	})
)

生命周期

  • 当元素的connectedCallback被首次调用时,Vue自定义元素会在其隐式根部挂载一个内部的Vue组件实例。
  • 当元素的disconnectedCallback被调用时,Vue会在很短的时间后检查此元素是否已被移出页面。
    • 如果元素仍在文档中,说明是移动,组件实例将被保留;
    • 如果元素已被移出文档,说明是移除,组件实例将被卸载。

Props

  • 所有使用props选项声明的prop都将在自定义元素上定义为property。Vue将在合适的时候自动处理attribute/property之间的映射。
    • Attribute总是映射为相应的property
    • 基础类型(string,boolean或number)的peoperty会被映射为attribute
  • Vue也会自动将声明为Boolean或Number类型的attribute prop(始终为字符串)转换为所需的类型,例如给出以下prop声明:
props:{
	selected:Boolean,
	index:Number,
}

在组件中,selected会被转换为true,index会被转换为1

事件

在自定义元素中,通过this.$emit或在setup中的emit发出的事件会被调度为原生CustomEvents。附加的事件参数(payload)会作为数组暴露在CustomEvent对象的detail property上。

插槽

在组件内部,可以像往常一样使用<slot/>渲染插槽。但是在解析最终生成的元素时,它只接受原生插槽语法

  • 不支持作用域插槽
  • 传递命名插槽时,请使用slot attribute而非v-slot指令:
<my-element>
	<div slot='named'>hello</div>
</my-element>

Provide/Inject

Provide/Inject API和组合式API中的Provide/Inject 在Vue定义的自定义元素之间可以正常工作。但是请注意这只适用于自定义元素之间,即Vue定义的自定义元素将无法注入非自定义元素的Vue组件提供的属性。

将SFC作为自定义元素

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选项传递给对应的构造插件:

  • @vitejs/plugin-vue
  • vue-loader

Vue自定义元素库的提示

 类似资料: