一、前言
v-slot
指令自 Vue 2.6.0 起被引入,提供更好的支持slot
和slot-scope
特性的 API 替代方案。v-slot
完整的由来参见这份 RFC。
在接下来所有的 2.x 版本中slot
和slot-scope
特性仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。
因此以下原本的写法在未来将会被废弃
<base-layout>
<template slot="header">
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
<template slot="footer">
<p>Here's some contact info</p>
</template>
</base-layout>
二、插槽的分类
- 匿名插槽(或默认插槽): 只能有一个
- 具名插槽: slot使用name命名
- 作用域插槽: 父组件用于接收子组件内数据
Foo.vue 父组件
<template>
<div class="tmpl">
<h1>父子组件与兄弟组件通信</h1>
<h3>子组件通信为 {{childMsg}}</h3>
<my-bar :message="parentMsg" v-on:showMsg="getMsg">
<template #default>
<ul>
<li>匿名插槽内容11</li>
<li>匿名插槽内容22</li>
<li>匿名插槽内容33</li>
</ul>
</template>
</my-bar>
<my-baz>
<template v-slot:header="items">
<p>具名插槽 header</p>
<p>作用域插槽: {{items.user.name}}</p>
</template>
<template v-slot:default>
<p>匿名插槽内容</p>
</template>
<template v-slot:footer>
<p>具名插槽 footer</p>
</template>
</my-baz>
</div>
</template>
<script>
import MyBar from '@/components/Foo/Bar.vue'
import MyBaz from '@/components/Foo/Baz.vue'
export default {
name: 'Foo',
components: {
MyBar, MyBaz
},
data () {
return {
parentMsg: 'abc123',
childMsg: ''
}
},
methods: {
getMsg: function (data) {
this.childMsg = data
}
}
}
</script>
<style scoped>
h1 {
font-weight: normal;
}
</style>
匿名插槽 Bar.vue 子组件
<template>
<div class="tmpl">
<h1>父组件传值为 {{message}}</h1>
<input type="text" placeholder="请输入子组件内容" v-model="param">
<button @click="sendParam">通信父组件</button>
<slot></slot>
</div>
</template>
<script>
import EventHandler from '@/assets/js/EventHandler.js'
export default {
name: 'Bar',
props: {
message: {
type: String,
default: '默认为空'
}
},
data () {
return {
param: ''
}
},
methods: {
sendParam: function () {
var param = this.param
this.$emit('showMsg', param)
EventHandler.$emit('showMsg', param)
}
}
}
</script>
<style scoped>
h1 {
font-weight: normal;
}
.tmpl {
border: 1px solid #333;
padding: 30px;
}
</style>
具名&作用域插槽 Baz.vue 子组件
<template>
<div class="tmpl">
<h1>Baz</h1>
<h1>兄弟组件通信为{{brotherMsg}}</h1>
<slot name="header" :user="user">
{{ user.name }}
</slot>
<slot></slot>
<slot name="footer"></slot>
</div>
</template>
<script>
import EventHandler from '@/assets/js/EventHandler.js'
export default {
name: 'Baz',
mounted () {
var that = this
EventHandler.$on('showMsg', function (data) {
that.brotherMsg = data
})
},
data () {
return {
brotherMsg: '',
user: {
name: '李四',
age: 15
}
}
}
}
</script>
<style scoped>
h1 {
font-weight: normal;
}
.tmpl {
border: 1px solid #333;
padding: 30px;
margin-top: 30px;
}
</style>
三、简写技巧
具名插槽的缩写
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header
<template v-slot:footer>
<p>具名插槽 footer</p>
</template>
<!-- 等价于 -->
<template #footer>
<p>具名插槽 footer</p>
</template>
...
...
<current-user #default="{ user }">
{{ user.firstName }}
</current-user>