一、如何让属性自动传递?
假设我们封装了一个带样式的输入框组件,但直接使用时发现输入没反应:
<!-- 父组件 -->
<Child></Child>
<!-- 子组件 -->
<el-input></el-input> <!-- 这里缺少v-model绑定 -->
解决方案:用$attrs自动收快递
就像快递员会把包裹自动送到你家,$attrs能自动接收父组件传来的所有属性:
<!-- 父组件 -->
<Child v-model="keyword"></Child>
<!-- 子组件 -->
<el-input v-bind="$attrs"></el-input> <!-- 自动接收所有属性 -->
小实验:看看$attrs怎么工作
<!-- 父组件传三个属性 -->
<Child a="苹果" b="香蕉" c="橙子"></Child>
<!-- 子组件只收a和b -->
<script>
props: ['a', 'b'], // 这里只收a和b
setup(props, { attrs }) {
console.log(attrs) // 这里会显示{c: "橙子"}
}
</script>
二、如何让插槽也能穿透?
当需要在输入框里添加图标时,原组件的插槽要怎么穿透?
解决方法:用$slots转发插槽
就像转交快递包裹一样,把插槽原样转发:
<!-- 父组件 -->
<Child>
<template #prepend>🔍 搜索</template>
</Child>
<!-- 子组件 -->
<el-input>
<!-- 循环转发所有插槽 -->
<template v-for="(_, name) in $slots" #[name]>
<slot :name="name"></slot>
</template>
</el-input>
需要传数据怎么办?(作用域插槽)
当插槽里需要显示子组件内部的数据时:
<!-- 父组件 -->
<Child>
<template #prepend="{ message }">{{ message }}</template>
</Child>
<!-- 子组件 -->
<script>
setup() {
const message = ref('请输入内容')
return { message }
}
</script>
<el-input>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData"></slot>
</template>
</el-input>
三、如何直接操作内部元素?
想通过ref让输入框自动聚焦怎么办?
解决方法:搭桥接驳
<!-- 父组件 -->
<Child ref="myInput"></Child>
<script>
// 点击按钮时触发聚焦
myInput.value.inputRef.focus()
</script>
<!-- 子组件 -->
<el-input ref="inputRef"></el-input>
<script>
// 暴露内部输入框的引用
defineExpose({
inputRef
})
</script>
demo
/** 父组件 */
<template>
<Child ref="childRef" v-model="value">
<template #prepend="{ msg }"> 测试插槽传递 {{ msg }}</template>
</Child>
</template>
<script setup>
import Child from './components/child.vue'
import { ref, onMounted } from 'vue'
const value = ref()
const childRef = ref()
onMounted(() => {
// 调用子组件中的el-input的focus方法
childRef.value.inputRef.focus()
})
</script>
/** 子组件 */
<template>
<div class="my-input">
<el-input ref="inputRef" v-bind="$attrs">
<!-- name就是插槽名称 -->
<template v-for="(_, name) in $slots" #[name]>
<slot :name="name" v-bind="message"></slot>
</template>
</el-input>
</div>
</template>
<script setup>
import { ref } from 'vue'
defineProps(() => ['a', 'b'])
const message = ref({ msg: '作用域插槽传值' })
const inputRef = ref()
// 暴露出el-input实例
defineExpose({
inputRef
})
</script>
常见问题解答
- 为什么用
$attrs还要加v-bind?
就像拆快递需要先签收,v-bind相当于把收到的属性拆包给内部组件 - 循环插槽会降低性能吗?
就像转发快递不会改变包裹内容,Vue内部有优化机制,不会影响性能 - 直接暴露内部元素安全吗?
建议用defineExpose只暴露需要的方法,就像只给快递员开小门取件
通过这三个技巧,你可以像搭积木一样轻松封装组件,还能保持原有组件的全部功能!