插槽-slot
Slot在组件模板中占位,当使用该组件时,组件里的内容会替换组件模板中slot的位置,作为承载分发内容的出口 插槽可以让用户更好地扩展组件,更好地复用组件以及做一些定制化的处理
例如父组件在使用到一个复用组件的时候,不同场景下这个组件有少量表现不一样,如果去重写组件是一件不明智的事情
通过slot插槽向组件内部指定位置传递内容,让这个复用组件能在不同场景下适用 如:布局组件、表格列、下拉选、弹框显示内容等
1.slot使用
-
默认slot使用 子组件里slot标签确认渲染的位置,标签里可以放一些dom结构,当父组件使用时,如果没用往插槽中传入内容,slot标签内的dom结构就会显示页面
-
具名slot使用 子组件用v-slot来表示插槽的名字,不传则使用默认插槽 父组件中使用时在默认插槽的基础上加上slot属性,值为子组件插槽v-slot的属性值
父:
子:
页面上显示的顺序由父来决定,也就是说在子中把header放在最后面,最终在页面上也是head-content-bottom的顺序
- 作用域slot使用
组件间传递数据
2.slot实现原理
slot本质上是返回VNode的函数,一般情况下,Vue中的组件要渲染到页面上需要经过template => render function => VNode => DOM
比如一个带slot的组件
Vue.component('button-counter', {
template: '<div> <slot>我是默认内容</slot></div>'
})
new Vue({
el: '#app',
template: '<button-counter><span>我是slot传入内容</span></button-counter>',
components:{buttonCounter}
})
经过vue编译, 组件渲染函数会变成这样
(function anonymous(
) {
with(this){return _c('div',[_t("default",[_v("我是默认内容")])],2)}
})
而这个_t就是slot渲染函数:
function renderSlot (
name,
fallback,
props,
bindObject
) {
// 得到渲染插槽内容的函数
var scopedSlotFn = this.$scopedSlots[name];
var nodes;
// 如果存在插槽渲染函数,则执行插槽渲染函数,生成nodes节点返回
// 否则使用默认值
nodes = scopedSlotFn(props) || fallback;
return nodes;
}
而scopedSlots其实就是递归解析各个节点, 获取slot
function resolveSlots (
children,
context
) {
if (!children || !children.length) {
return {}
}
var slots = {};
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i];
var data = child.data;
// remove slot attribute if the node is resolved as a Vue slot node
if (data && data.attrs && data.attrs.slot) {
delete data.attrs.slot;
}
// named slots should only be respected if the vnode was rendered in the
// same context.
if ((child.context === context || child.fnContext === context) &&
data && data.slot != null
) {
// 如果slot存在(slot="header") 则拿对应的值作为key
var name = data.slot;
var slot = (slots[name] || (slots[name] = []));
// 如果是tempalte元素 则把template的children添加进数组中,这也就是为什么你写的template标签并不会渲染成另一个标签到页面
if (child.tag === 'template') {
slot.push.apply(slot, child.children || []);
} else {
slot.push(child);
}
} else {
// 如果没有就默认是default
(slots.default || (slots.default = [])).push(child);
}
}
// ignore slots that contains only whitespace
for (var name$1 in slots) {
if (slots[name$1].every(isWhitespace)) {
delete slots[name$1];
}
}
return slots
}