Vue插槽 2.6版本
Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot>
元素作为承载分发内容的出口。
插槽(slot)的概念很简单,其实就是分发内容。
插槽分为:普通插槽、作用域插槽、默认插槽
插槽的作用:传递复杂内容的方式
作用域插槽的函数理解:
作用域插槽则可以理解为,本质上是返回组件的函数,我们可以把下面一整段标签当成是一个函数:
普通插槽
<body>
<div id="app">
<my-ul title="this is my ul">
<!-- 插槽传递文本 -->
this is slot
<!-- 插槽传递标签 -->
<p> this is slot -- p </p>
<!-- 插槽传递组件 -->
<my-li></my-li>
</my-ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('my-li', {
template: ` <li>My li</li>`
})
Vue.component('my-ul', {
props: {
title: String
},
template: `
<ul>
{{ title }}
<li>li1</li>
<li>li2</li>
<!-- 子组件通过 slot 标签接受传递过来的内容 -->
<slot></slot>
</ul>
`
});
var vm = new Vue({
el: '#app',
data() {
return {
property: 'value',
};
},
})
</script>
</body>
注:如果 <my-ul>
的 template
中没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃
编译作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
<body>
<div id="app">
<my-ul title="this is my ul">
<!-- 插槽传递文本 -->
this is slot
<!-- 插槽传递标签 -->
<p> this is slot -- p </p>
<!-- 插槽传递组件 -->
<!-- 这里的 liData 会渲染为 ‘根组件’ -->
<my-li>{{ liData }}</my-li>
</my-ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('my-li', {
template: ` <li>My li<slot></slot></li>`,
data() {
return {
liData: 'li组件',
};
},
})
Vue.component('my-ul', {
props: {
title: String
},
template: `
<ul>
{{ title }}
<li>li1</li>
<li>li2</li>
<slot></slot>
</ul>
`,
data() {
return {
liData: 'ul组件',
};
},
});
var vm = new Vue({
el: '#app',
data() {
return {
property: 'value',
liData: '根组件'
};
},
})
</script>
</body>
后备内容(默认值)
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
<body>
<div id="app">
<my-ul title="this is my ul">
<!-- 插槽传递文本 -->
this is slot
<!-- 插槽传递标签 -->
<p> this is slot -- p </p>
<!-- 插槽传递组件 -->
<!-- 这里会渲染为默认值 -->
<my-li></my-li>
<!-- 这里会渲染为传递值 -->
<my-li>传递值</my-li>
</my-ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('my-li', {
template: ` <li>My li<slot>默认值</slot></li>`,
})
Vue.component('my-ul', {
props: {
title: String
},
template: `
<ul>
{{ title }}
<li>li1</li>
<li>li2</li>
<slot></slot>
</ul>
`,
});
var vm = new Vue({
el: '#app',
data() {
return {
property: 'value',
};
},
})
</script>
</body>
具名插槽
<body>
<div id="app">
<my-ul title="this is my ul">
<!-- 在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提 供其名称 -->
<!-- v-slot:后不能加空格 -->
<template v-slot:title>
slot --- title
</template>
<template v-slot:footer>
slot --- footer
</template>
<p> this is slot -- p </p>
</my-ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('my-ul', {
props: {
title: String
},
template: `
<ul>
<!-- <slot> 元素有一个特殊的 attribute:name。通过name属性与v-slot的值对应 -->
<li><slot name="title"></slot></li>
<!-- 不带 name 的 <slot> 出口会带有隐含的名字“default”。可写可不写 -->
<!-- 任何没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容。 -->
<li><slot></slot></li>
<li><slot name="footer"></slot></li>
</ul>
`,
});
var vm = new Vue({
el: '#app',
data() {
return {
property: 'value',
};
},
})
</script>
</body>
作用域插槽
让插槽内容能够访问子组件中才有的数据
作用域插槽的内部工作原理是将你的插槽内容包裹在一个拥有单个参数的函数
<body>
<div id="app">
<my-ul title="this is my ul">
<!-- 绑定在 <slot> 元素上的 attribute 被称为插槽 prop。 -->
<!-- 在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字 -->
<!-- 在这个例子中 'scope' 就是 slot 传递过来的属性对象 -->
<template v-slot:title="scope">
<!-- 通过 scope.user 获取 slot 中 user 属性的绑定值 -->
{{ scope.user.firstName }}
</template>
<template v-slot:footer>
slot --- footer
</template>
<p> this is slot -- p </p>
</my-ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('my-ul', {
data() {
return {
user: {
firstName: 'firstName',
lastName: 'lastName'
}
};
},
props: {
title: String
},
template: `
<ul>
<!-- 通过设置 slot 的自定义属性传递值给父级作用域 -->
<li><slot name="title" :user="user"></slot></li>
<li><slot></slot></li>
<li><slot name="footer"></slot></li>
</ul>
`,
});
var vm = new Vue({
el: '#app',
data() {
return {
property: 'value',
};
},
})
</script>
</body>
当被提供的内容只有默认插槽时,组件的标签就可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上:
<my-ul title="this is my ul" v-slot="scope">
{{ scope.user.firstName }}
<p> this is slot -- p </p>
</my-ul>
// 注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确
解构插槽 Props
<body>
<div id="app">
<my-ul title="this is my ul">
<!-- 解构传入的 prop -->
<template v-slot:title="{ user }">
{{ user.firstName }}
</template>
<!-- 结构并重命名 -->
<template v-slot:footer="{ user: footerProps }">
{{ footerProps.lastName }}
</template>
<!-- 设置默认值 用于插槽 prop 是 undefined 的情形 -->
<template v-slot="{ user = { firstName: '默认firstName' } }">
{{ user.firstName }}
</template>
</my-ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
Vue.component('my-ul', {
data() {
return {
user: {
firstName: 'firstName',
lastName: 'lastName'
}
};
},
props: {
title: String
},
template: `
<ul>
<!-- 通过设置 slot 的自定义属性传递值给父级作用域 -->
<li><slot name="title" :user="user"></slot></li>
<li><slot></slot></li>
<li><slot name="footer" :user="user"></slot></li>
</ul>
`,
});
var vm = new Vue({
el: '#app',
data() {
return {
property: 'value',
};
},
})
</script>
</body>
动态插槽名
<my-ul>
<!-- 这里的 dynamicSlotName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用 -->
<template v-slot:[dynamicSlotName]>
...
</template>
</my-ul>
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
<my-ul title="this is my ul">
<!-- v-slot:title >> #title -->
<template #title="{ user }">
{{ user.firstName }}
</template>
<!-- <!-- v-slot:footer >> #footer -->
<template #footer="{ user: footerProps }">
{{ footerProps.lastName }}
</template>
<!-- 该缩写只在其有参数的时候才可用 -->
<!-- 下面的写法会报错 -->
<template #="{ user = { firstName: '默认firstName' } }">
{{ user.firstName }}
</template>
<!-- 可以替换为 -->
<template #default="{ user = { firstName: '默认firstName' } }">
{{ user.firstName }}
</template>
</my-ul>