1. 插槽的本质
组件能够接收任意类型的 JavaScript 值作为 props,但组件要如何接收模板内容呢?插槽可以理解为模板提供内容,<slot>
元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 (slot content) 将在哪里被渲染。
那么插槽到底是怎么实现替换,怎么运行的。
先看一个案例: 子组件通过setup中slots接收,使用createElementVNode
进行渲染。
父组件:
vue
<SlotDemo>
<template v-slot:default="{ title }">
{{ title }}
</template>
</SlotDemo>
子组件:
vue
import { createElementVNode } from 'vue';
export default {
setup(props, {slots}) {
const _default = slots.default()
console.log('打印***_default', _default)
return () => {
return createElementVNode('div', {}, [_default])
}
}
}
</script>
子组件定义了默认插槽,父组件传入默认插槽的内容,子组件使用setup语法进行接收,可以看到slots本质是一个对象,对象的键就是插槽的名称,对象的值是一个函数,作用域插槽需要传递数据,就是这个函数的参数。
可以打印下slots:
运行这个函数,得到虚拟节点:
子组件可以通过createElementVnode进行渲染
这样就可以看出插槽的
2. 插槽的使用
2.1 默认插槽
子组件 使用默认或者写
js
<slot name="default"></slot>
// 或者
<slot title="默认插槽"></slot>
父组件 三种写法
js
// 直接写
默认插槽
// 或者
<template v-slot:default>
默认插槽(v-slot:default)
</template>
// 或者
<template #default>
默认插槽(v-slot:default)
</template>
2.2 具名插槽
默认插槽不满足使用需求,那么具名插槽可以解决,为每一个插槽添加名称,就可以进行区分。
子组件:
js
<slot name="header"></slot>
父组件:
js
<template #header>
具名插槽
</template>
// 或者
<template v-slot:header>
具名插槽
</template>
2.3 作用域插槽
如果子组件的内容需要通过插槽传递到外部父组件的插槽中,那么作用域插槽就可以解决问题。我们可以为插槽添加属性,注意name字段是不可以添加的,因为已经被定义为插槽的名称,多个属性会收集成一个对象,传递给父组件的模版中,父组件通过v-slot:xxx=obj接受,这里可以解构
子组件:
js
<slot name="content" v-bind="{ a: 1, b: 2 }" />
// 或者
<slot name="content" :a="1" :b="2" />
父组件:
js
<template #content="obj">
作用域插槽{{ obj.a }}
</template>
// 或者
<template v-slot:content="{ a, b }">
作用域插槽{{ a }}
</template>
注意:插槽传递的内容,其他插槽是不可以使用的
2.4 动态插槽
我们常见的ui组件库element表格,操作栏可以我们自己定义插槽。使用的就是动态插槽。
如果插槽的名称需要外部自定义,那么动态插槽可以满足要求,动态插槽是用外部传入的名称,定义子组件插槽的名称。通过子组件定义:name="dynamicName"
父组件v-slot:[dynamicName]
书写。
子组件:
js
<slot :name="dynamicName" :a="1" :b="2"></slot>
父组件:
js
<template v-slot:[dynamicName]="{ a, b }">
动态插槽{{ a }}-{{ b }}
</template>
3. 总结
最后总结一下:插槽的实现就是对于子组件来说就是一个对象,键是插槽的名称,值是一个函数,用于渲染父组件传递的内容。
插槽的使用有几种:
- 默认插槽:如果子组件没有具名插槽,那么父组件提供的内容将被插入到子组件的默认插槽中。
- 具名插槽 :如果需要多个插槽及对应不同的内容,可以为插槽添加名称,并通过
<template v-slot:xxx>
和<slot name="xxx">
进行传递。 - 作用域插槽:允许子组件通过插槽向父组件传递数据。通过在插槽内部定义属性,并通过父组件提供的对象将数据传递给父组件来实现。
- 动态插槽 :允许父组件动态地指定插槽的名称。子组件可以使用动态的名称来定义插槽,并在父组件中通过
v-slot:[dynamicName]
来指定插槽。
如有错误,请指正O^O!