目录
一、引言
二、默认插槽
2.1. 默认插槽基本语法
2.2. 完整代码
2.2.1. main.js
2.2.2. App.vue
2.2.3. MyDialog.vue
2.3. 运行效果
三、插槽后备内容(默认值)
3.1. 插槽后备内容基本语法
3.2. 完整代码
3.2.1. main.js
3.2.2. App.vue
3.2.3. MyDialog.vue
3.3. 运行效果
四、具名插槽
4.1. 具名插槽语法
4.2. 完整代码
4.2.1. main.js
4.2.2. App.vue
4.2.3. MyDialog.vue
五、作用域插槽
5.1. 作用域插槽语法
5.2. 完整代码
5.2.1. main.js
5.2.2. App.vue
5.2.3. MyTable.vue
一、引言
如下图所示的两个对话框,除了内部的提示语信息内容有一些差异外,其他部分完全相同。这种情况下我们可以将对话框相同公用的部分抽取封装成一个组件,内部的会变动的提示语信息(组件的内容)通过插槽进行定制化传入参数,从而实现组件的复用。
插槽作用:让组件内部的一些结构支持自定义。
二、默认插槽
2.1. 默认插槽基本语法
1. 组件内需要定制的结构部分,改用<slot></slot>占位
2. 使用组件时, <MyDialog></MyDialog>标签内部, 传入结构替换slot
2.2. 完整代码
2.2.1. main.js
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({render: h => h(App),
}).$mount('#app')
2.2.2. App.vue
<template><div><!-- 2. 在使用组件时,组件标签内填入内容 --><MyDialog><div>你确认要删除么</div></MyDialog><MyDialog><p>你确认要退出么</p></MyDialog></div>
</template><script>
import MyDialog from './components/MyDialog.vue'
export default {data () {return {}},components: {MyDialog}
}
</script><style>
body {background-color: #b3b3b3;
}
</style>
2.2.3. MyDialog.vue
<template><div class="dialog"><div class="dialog-header"><h3>友情提示</h3><span class="close">✖️</span></div><div class="dialog-content"><!-- 1. 在需要定制的位置,使用slot占位 --><slot></slot></div><div class="dialog-footer"><button>取消</button><button>确认</button></div></div>
</template><script>
export default {data () {return {}}
}
</script><style scoped>
* {margin: 0;padding: 0;
}
.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}
.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}
.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}
.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}
.dialog-footer {display: flex;justify-content: flex-end;
}
.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}
.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
</style>
2.3. 运行效果
三、插槽后备内容(默认值)
3.1. 插槽后备内容基本语法
我们通过插槽实现内容的定制,同时插槽还支持设定默认的内容,即插槽后备内容。我们在封装组件时,可以为预留的`<slot>` 插槽提供后备内容(默认内容)。
外部使用组件时,不传东西,则slot会显示后备内容
外部使用组件时,传东西了,则slot整体会被换掉
3.2. 完整代码
3.2.1. main.js
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({render: h => h(App),
}).$mount('#app')
3.2.2. App.vue
<template><div><MyDialog></MyDialog><MyDialog><p>你确认要退出么</p></MyDialog></div>
</template><script>
import MyDialog from './components/MyDialog.vue'
export default {data () {return {}},components: {MyDialog}
}
</script><style>
body {background-color: #b3b3b3;
}
</style>
3.2.3. MyDialog.vue
<template><div class="dialog"><div class="dialog-header"><h3>友情提示</h3><span class="close">✖️</span></div><div class="dialog-content"><!-- 插槽默认显示的内容 --><slot>插槽默认显示的内容</slot></div><div class="dialog-footer"><button>取消</button><button>确认</button></div></div>
</template><script>
export default {data () {return {}}
}
</script><style scoped>
* {margin: 0;padding: 0;
}
.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}
.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}
.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}
.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}
.dialog-footer {display: flex;justify-content: flex-end;
}
.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}
.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
</style>
3.3. 运行效果
四、具名插槽
前面我们讲的默认插槽,它只支持组件内只有一处内容不同需要自定义的情况。但我们在实际开发过程中经常会碰到组件中有多处内容不同需要自定义,这时我们可以使用具名插槽来实现。
4.1. 具名插槽语法
1. 多个slot使用name属性区分名字
2. template配合v-slot:名字来分发对应标签
4.2. 完整代码
4.2.1. main.js
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({render: h => h(App),
}).$mount('#app')
4.2.2. App.vue
<template><div><MyDialog><!-- 需要通过template标签包裹需要分发的结构,包成一个整体 --><template v-slot:head><div>我是大标题</div></template><template v-slot:content><div>我是内容</div></template><template #footer><button>取消</button><button>确认</button></template></MyDialog></div>
</template><script>
import MyDialog from './components/MyDialog.vue'
export default {data () {return {}},components: {MyDialog}
}
</script><style>
body {background-color: #b3b3b3;
}
</style>
4.2.3. MyDialog.vue
<template><div class="dialog"><div class="dialog-header"><!-- 一旦插槽起了名字,就是具名插槽,只支持定向分发 --><slot name="head"></slot></div><div class="dialog-content"><slot name="content"></slot></div><div class="dialog-footer"><slot name="footer"></slot></div></div>
</template><script>
export default {data () {return {}}
}
</script><style scoped>
* {margin: 0;padding: 0;
}
.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}
.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}
.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}
.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}
.dialog-footer {display: flex;justify-content: flex-end;
}
.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}
.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
</style>
五、作用域插槽
我们在企业级项目开发过程中,经常会碰到PC端管理后台中有些列表的列头和列题在不同的条件下会动态的变更,以外的开发实现上可能会使用判断条件语句来实现,如果动态变更的种类条件很多的情况下,代码就会相当复杂和冗余。另外还有列表中最右侧列,有些全是查看按钮,有些全是删除按钮。在Vue中,这些需求通过作用域插槽实现起来就非常简洁。
使用具名插槽结合作用域插槽实现动态表格:
使用默认插槽结合作用域插槽实现动态操作按钮:
注意:作用域插槽并不是插槽的一个种类,插槽只有默认插槽和具名插槽两种。作用域插槽它是插槽的一种传参语法。
作用域插槽的意思是在定义 slot 插槽的时候, 我们还可以传值。给插槽上可以绑定数据,将来使用组件时可以用。
如下的场景需求:封装表格组件
5.1. 作用域插槽语法
1. 给 slot 标签, 以添加属性的方式传值
2. 所有添加的属性, 都会被收集到一个对象中
3. 在父组件的template中, 通过 ` #插槽名= "obj" ` 接收子组件中插槽的传值,默认插槽名为default
5.2. 完整代码
5.2.1. main.js
import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falsenew Vue({render: h => h(App),
}).$mount('#app')
5.2.2. App.vue
<template><div><MyTable :data="list"><!-- 3. 通过template #插槽名="变量名" 接收 MyTable插槽的传参,传过来的参数是个如下形式的obj --><!-- {row: { id: 2, name: '孙红雷', age: 19 },msg: '测试文本'} --><template #default="obj"><button @click="del(obj.row.id)">删除</button></template></MyTable><MyTable :data="list2"><!-- { row } 为直接从obj从解构出来的对象 --><template #default="{ row }"><button @click="show(row)">查看</button></template></MyTable></div>
</template><script>
import MyTable from './components/MyTable.vue'
export default {data () {return {list: [{ id: 1, name: '李沁', age: 18 },{ id: 2, name: '虞书欣', age: 19 },{ id: 3, name: '新垣结衣', age: 17 },],list2: [{ id: 1, name: '胡歌', age: 18 },{ id: 2, name: '朱一龙', age: 19 },{ id: 3, name: '刘烨', age: 17 },]}},methods: {del (id) {this.list = this.list.filter(item => item.id !== id)},show (row) {// console.log(row);alert(`姓名:${row.name}; 年纪:${row.age}`)}},components: {MyTable}
}
</script>
5.2.3. MyTable.vue
<template><table class="my-table"><thead><tr><th>序号</th><th>演员</th><th>年纪</th><th>操作</th></tr></thead><tbody><tr v-for="(item, index) in data" :key="item.id"><td>{{ index + 1 }}</td><td>{{ item.name }}</td><td>{{ item.age }}</td><td><!-- 1. 给slot标签,添加多个属性的方式传值 --><slot :row="item" msg="测试文本"></slot><!-- 2. 将所有的属性,添加到一个对象中 --><!-- {row: { id: 2, name: '孙大明', age: 19 },msg: '测试文本'}--></td></tr></tbody></table>
</template><script>
export default {props: {data: Array}
}
</script><style scoped>
.my-table {width: 450px;text-align: center;border: 1px solid #ccc;font-size: 24px;margin: 30px auto;
}
.my-table thead {background-color: #1f74ff;color: #fff;
}
.my-table thead th {font-weight: normal;
}
.my-table thead tr {line-height: 40px;
}
.my-table th,
.my-table td {border-bottom: 1px solid #ccc;border-right: 1px solid #ccc;
}
.my-table td:last-child {border-right: none;
}
.my-table tr:last-child td {border-bottom: none;
}
.my-table button {width: 65px;height: 35px;font-size: 18px;border: 1px solid #ccc;outline: none;border-radius: 3px;cursor: pointer;background-color: #ffffff;margin-left: 5px;
}
</style>