属性透传与实传
实传:prop指定传递的属性; 透传:未指定prop传递的属性传递给子组件嵌套组件
使用场景: 以二次封装ui组件场景为例;保持第三方组件原有功能不变,实现一些定制效果
使用的api: $attrs, $listeners, inheritAttrs, 渲染函数
// 父组件
<MyButton name="测试透传" :disabled="false" @click="clickBtn" />
// 子组件, MyButton
const MyButton = {
// 将透传的属性继续传递给ui-Button组件
template: `<div class="box">
<Button v-bind:type="type" v-bind="$attrs" v-on="$listeners" />
</div>`,
components: {
Button,
},
inheritAttrs: false, // 禁止未被props指定的属性应用到组件根元素上
props: {
type: {
type: String,
default: 'primary', // 设置默认值为primary
},
},
created() {
console.log(this.$attrs); // {name: '测试透传', disabled: false}
console.log(this.$listeners); // {click: f}
},
};
// ui组件Button, 直接使用如下, 默认type为'default'
<Button type="primary" name="测试文本" icon="xxx" disabled @click="xxx" />
.native绑定事件到子组件根元素; $listeners可用于将事件监听器指向到具体的某个子元素,使用更灵活
引申使用:动态组件透传实现
在上面案例的基础上实现动态生成表单内容
// template
<div v-for="(config, index) in configArr" :key="config.type + index">
<!-- 动态组件,根据配置中的 Type 来决定渲染的是 Input 还是 Select -->
<component :is="config.type" :configProps="config.props"></component>
</div>
// configArr
[{
type: 'Input',
props: {
placeholder: '我是默认值',
clearable: true
}
}, {
type: 'Button',
props: {
type: 'default'
}
}]
// 注册新的组件, 使用渲染函数以替代动态组件
// 使用渲染函数解构props,获取attribute绑定
const CompFormItem = {
components: {
Input, Select
},
name: 'FormItem',
props: {
config: {
required: true
}
},
render (h) {
// 第一个参数就是配置中的 type,也就是我们的组件名称
return h(`${this.config.type}`, {
props: {
...this.config.props || {}
},
attrs: {
...this.config.props || {}
}
})
}
}
// 使用组件
<FormItem :config="config"></FormItem>
-----关于vue3.x的变更-----
$listeners被移除,事件监听器整合到$attrs中$attrs包含style、class属性- .native修饰符移除,因为子组件未定义触发的事件,将被作为子组件的根元素的原生事件监听,此时无需使用.native绑定原生事件到子组件根元素; 使用emits选项定义组件触发的事件
-----关于向父组件透传原生事件的问题-----
<template>
<button @click="$emit('click', $event)">OK</button>
</template>
<script>
export default {
emits: [] // 不声明事件
}
</script>
// 父组件
<my-button @click="handleClick"></my-button>
该事件触发两次:1. $emit;2. 应用到子组件根元素上的事件
解决方案:1. emits选项正确声明事件;2. 移除透传的事件