1.属性透传
利用 v-bind + $attr 实现属性方法继承传递
具体原理参考 juejin.cn/post/745685…
父组件
<template>
<ChildComponent id="child" class="custom-class" data-custom="123" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
}
};
</script>
ChildComponent子组件
<template>
<div v-bind="$attrs" >
</div>
</template>
2.v-model透传
<template>
<MidComponent v-model="parentData" />
<!--等价于 -->
<!-- <MidComponent :modelValue="parentData" @update:modelValue="$event => parentData = $event" /> -->
</template>
<script>
import MidComponent from './MidComponent.vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
MidComponent,
ChildComponent,
},
data() {
return {
parentData: 'xxxx'
};
}
};
</script>
中间组件需要显示声明回调的事件
<!-- 中间组件 -->
<template>
<ChildComponent v-bind="$attrs" />
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
inheritAttrs: false,
};
</script>
子组件需要 定义操作的属性+ 声明回调的事件
<!-- 子组件 -->
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: ['modelValue'],
};
</script>
el-input处理
只需要在子组件 对应修改 el-input的事件 model-value 和@input的参数 ,实现数据绑定即可
<!-- 子组件 -->
<template>
<el-input :model-value="modelValue" @input="$emit('update:modelValue', $event)" >
</el-input>
</template>
<script>
export default {
props: ['modelValue'],
};
</script>
3.v-model透传(createVNode)
使用createVNode创建的组件,事件的定义需要加上onXXX(以便于与浏览器原生事件做区分)
createVNode(ChildSubComponent, {
modelValue: props.modelValue,
'onUpdate:modelValue': onValueUpdate,
})
完整代码
ParentComponent.vue
<template>
<MidComponent v-model="parentData" />
</template>
<script>
import MidComponent from './MidComponent.vue';
export default {
components: {
MidComponent,
},
data() {
return {
parentData: 'xxxx',
};
}
}
};
</script>
MidComponent.vue
<script lang="ts">
import { createVNode, defineComponent } from 'vue'
import ChildComponent from './ChildComponent.vue';
export default defineComponent({
props: {
modelValue: {
required: true,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const onValueUpdate = (value: any) => {
emit('update:modelValue', value)
}
return () =>
createVNode(ChildComponent, {
modelValue: props.modelValue,
'onUpdate:modelValue': onValueUpdate,
})
},
})
</script>
ChildComponent.vue
<template>
<el-input :model-value="modelValue" @input="$emit('update:modelValue', $event)" >
</el-input>
</template>
<script>
export default {
props: ['modelValue'],
};
</script>
4.插槽透传
通过$slots 获取父组件所有声明的插槽,然后遍历输出,中间组件和子组件都要遍历
插槽穿透原理可以参考 juejin.cn/post/744884…
父组件
<template>
<MidComponent v-model="parentData">
<template #prefix>
111
</template>
<template #suffix>
222
</template>
<template #prepend>
333
</template>
</MidComponent>
</template>
<script>
import MidComponent from './MidComponent.vue';
export default {
components: {
MidComponent,
},
data() {
return {
parentData: 'xxxx'
};
}
};
</script>
中间组件
<!-- 中间组件 -->
<template>
<ChildComponent v-bind="$attrs" >
<template v-for="(_, name) in $slots" #[name]="scopedData">
<slot :name="name" v-bind="scopedData"></slot>
</template>
</ChildComponent>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
inheritAttrs: false,
};
</script>
子组件
<!-- 子组件 -->
<template>
<el-input :model-value="modelValue" @input="$emit('update:modelValue', $event)" >
<!--(_, name) 注意第一个是内容,第二个才是key 也就是插槽的名称 -->
<template v-for="(_, name) in $slots" #[name]="scopedData">
<slot :name="name" v-bind="scopedData"></slot>
</template>
</el-input>
</template>
<script>
export default {
props: ['modelValue'],
mounted() {
console.log(this.$slots);
}
};
</script>
5.获取ref以及暴露的方法
通过遍历ref的所有属性方法,然后直接绑定在this上,暂时只支持一层调用
父组件ParentComponent.vue
<template>
<ChildComponent ref="testRef" v-model="parentData" />
<input type="button" value="test" @click="test">
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent,
},
data() {
return {
parentData: 'xxxx'
};
},
methods: {
test() {
this.$refs.testRef.focus2()
console.log(this.$refs.testRef.val)
}
}
};
</script>
子组件ChildComponent.vue
<!-- 子组件 -->
<template>
<el-input ref="myRef" :model-value="modelValue" @input="$emit('update:modelValue', $event)" >
</el-input>
</template>
<script>
export default {
props: ['modelValue'],
data() {
return {
val:111
}
},
mounted() {
for (const key in this.$refs.myRef) {
this[key] = this.$refs.myRef[key]
}
},
methods: {
focus2() {
this.$refs.myRef.focus()
}
}
};
</script>