目录
一、组件插槽
1.1、单个插槽
2.2、具名插槽
2.3、作用域插槽
插槽,我要钻到你的怀里
默认插槽
具名插槽
作用域插槽
插槽默认值
一、组件插槽
组件的最大特性就是 复用性 ,而用好插槽能大大提高组件的可复用能力
1.1、单个插槽
当子组件模板只有一个没有属性的插槽时,父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,并 替换 掉插槽标签本身。
2.2、具名插槽
有时我们需要多个插槽,来完成对应的数据自定义显示。
一个不带 name 的 <slot> 出口会带有隐含的名字 “default” 。
自 2.6.0 起有所更新。已废弃的使用 slot
# 子组件
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
</div>
# 父组件
<app-layout>
// 老写法
<h1 slot="header">这里可能是一个页面标题</h1>
// 新写法
// v-slot 只能添加在 <template> 上
// 简写 v-slot:header == #header
<template v-slot:header>
<h1>这里可能是一个页面标题</h1>
</template>
<p>主要内容的一个段落。</p>
</app-layout>
2.3、作用域插槽
作用域插槽 是一种特殊类型的插槽,用作一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件一样
<div id="app">
<child :users='users'>
<!--
vue2.6之前
-->
<!-- <h6 slot="action" slot-scope="row" @click="pdel(row)">删除一下</h6> -->
<!--
vue2.6之后
-->
<!-- <template v-slot:action="row">
<h3 @click="pdel(row)">删除</h3>
</template> -->
<template #action="row">
<h3 @click="pdel(row)">删除</h3>
</template>
</child>
</div>
<script type="text/template" id="userhtml">
<div>
<table width="600" border="1">
<tr>
<th>ID</th>
<th>姓名</th>
<th>操作</th>
</tr>
<tr v-for="(item,index) in users" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>
<!-- 通过slot传数据,此slot为作用域插槽 -->
<slot name="action" :info="item">
<button @click="del(index)">删除</button>
</slot>
</td>
</tr>
</table>
</div>
</script>
<script>
const child = {
props: ['users'],
name: 'child',
template: '#userhtml',
methods: {
del(index) {
console.log(index)
}
}
}
const vm = new Vue({
el: '#app',
data() {
return {
users: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' },
]
}
},
components: { child },
methods: {
pdel(row) {
console.log(row.info)
}
}
})
</script>
插槽,我要钻到你的怀里
插槽,相信每一位
Vue
都有使用过,但是如何更好的去理解插槽,如何去自定义插槽,今天小编为你带来更形象的说明。默认插槽
大学毕业刚上班,穷鬼一个,想着每个月租房还要掏房租,所以小编决定买一个一居室,东拼西凑借了一堆债,终于凑够了首付,买了一个小小的毛坯房。我们可以把这个一居室的毛坯房想想成一个组件,这个房子的户型,面积,楼层都是固定的,但是室内如何装修,摆什么家具,这个却是由你来决定的,房间内部就可以理解为插槽,允许用户去自定义内容。
1. 开发商终于将一居室开发完交房了
<template> <!--这是一个一居室--> <div class="one-bedroom"> <!--添加一个默认插槽,用户可以在外部随意定义这个一居室的内容--> <slot></slot> </div> </template>
2. 小编要开始装修了
<template> <!--这里一居室--> <one-bedroom> <!--将家具放到房间里面,组件内部就是上面提供的默认插槽的空间--> <span>先放一个小床,反正没有女朋友</span> <span>再放一个电脑桌,在家还要加班写bug</span> </one-bedroom> </template> <script> import OneBedroom from '../components/one-bedroom' export default { components: { OneBedroom } } </script>
具名插槽
过了几年,小编有了女朋友,准备结婚了,一居室房间肯定不行啊,丈母娘嫌小不同意,没办法,只能又凑钱买大房子,买了一个两居室(穷逼一个),因为是两居室,所以有了主卧和次卧之分,装修是否也不能把主卧和次卧装修的一模一样,所以就需要进行区分。将房子想想成组件,那么组件就有两个插槽,并且需要起名字进行区分。
1. 开发商终于开发完交房了
<template> <div class="two-bedroom"> <!--这是主卧--> <div class="master-bedroom"> <!---主卧使用默认插槽--> <slot></slot> </div> <!--这是次卧--> <div class="secondary-bedroom"> <!--次卧使用具名插槽--> <slot name="secondard"></slot> </div> </div> </template>
2. 小编要卖血攒钱装修了
<template> <two-bedroom> <!--主卧使用默认插槽--> <div> <span>放一个大床,要结婚了,嘿嘿嘿</span> <span>放一个衣柜,老婆的衣服太多了</span> <span>算了,还是放一个电脑桌吧,还要写bug</span> </div> <!--次卧,通过v-slot:secondard 可以指定使用哪一个具名插槽, v-slot:secondard 也可以简写为 #secondard--> <template v-slot:secondard> <div> <span>父母要住,放一个硬一点的床,软床对腰不好</span> <span>放一个衣柜</span> </div> </template> </two-bedroom> </template> <script> import TwoBedroom from '../components/slot/two-bedroom' export default { components: { TwoBedroom } } </script>
作用域插槽
装修的时候,装修师傅问我洗衣机是要放到卫生间还是阳台,一般情况下开发商会预留放洗衣机的位置。而这个位置可以理解为插槽传的参数,这个就是作用域插槽。
1. 看一下卫生间插槽传了什么参数
<template> <div class="two-bedroom"> <!--其他内容省略--> <div class="toilet"> <!--通过v-bind 可以向外传递参数, 告诉外面卫生间可以放洗衣机--> <slot name="toilet" v-bind="{ washer: true }"></slot> </div> </div> </template>
2. 把洗衣机放到卫生间
<template> <two-bedroom> <!--其他省略--> <!--卫生间插槽,通过v-slot="scope"可以获取组件内部通过v-bind传的值--> <template v-slot:toilet="scope"> <!--判断是否可以放洗衣机--> <span v-if="scope.washer">这里放洗衣机</span> </template> </two-bedroom> </template>
插槽默认值
小编的同事不想等期房,所以就买了二手房,二手房前业主都装修好了,可以直接入住。当然也可以重新装修,下面是同事买的二手房。
1. 这是装修好的二手房
<template> <div class="second-hand-house"> <div class="master-bedroom"> <!--插槽可以指定默认值,如果外部调用组件时没有修改插槽内容,则使用默认插槽--> <slot> <span>这里有一张水床,玩的够嗨</span> <span>还有一个衣柜,有点旧了</span> </slot> </div> <!--这是次卧--> <div class="secondary-bedroom"> <!--次卧使用具名插槽--> <slot name="secondard"> <span>这里有一张婴儿床</span> </slot> </div> </div> </template>
2. 同事决定先把主卧装修了,以后结婚用
<second-hand-house> <!--主卧使用默认插槽,只装修主卧--> <div> <span>放一个大床,要结婚了,嘿嘿嘿</span> <span>放一个衣柜,老婆的衣服太多了</span> <span>算了,还是放一个电脑桌吧,还要写bug</span> </div> </second-hand-house>
自己项目中的使用 , 仅供参考 :
比如我这里有很多页面的头部都一样,所以这里我们可以利用插槽的复用性来进行一下代码的简化 :
<template>
<div>
<div class="header">
<img src="../assets/image/LOGO.png">
</div>
<!--具名插槽-->
<slot name="content"></slot>
</div>
</div>
</template>
<script>
export default {}
</script>
使用 :
<template>
<div id="dwBodyUser">
<public-header>
<el-tabs
v-model="activeName"
@tab-click="handleClick"
slot="content"
>
<el-tab-pane
label="所有事项"
name="first"
:model="formData"
ref="formDatas"
>
<!-- 所有事项组件 -->
<all-matters></all-matters>
</el-tab-pane>
<el-tab-pane
label="场景共享"
name="second"
:model="formData"
ref="formDatas"
>
<!-- 场景共享组件 -->
<scene-sharing></scene-sharing>
</el-tab-pane>
</el-tabs>
</public-header>
</div>
</template>
<script>
import publicHeader from '@/components/publicHeader/index.vue'
import allMatters from './component/allMattersTab.vue'
import screnSharing from './component/sceneSharingTab.vue'
export default {
components: {
publicHeader, // 公共头部组件
allMatters, // 所有事项组件
sceneSharing, // 场景共享组件
}
}
</script>
还有一个 公共 Table 示例 :
<!-- 作者 : 小灰狼
功能 : 所有事项
时间 : 2022/03 -->
<template>
<div>
<el-table
v-loading="loading"
:data="tableList"
style="100%"
:header-cell-style="{ background: '#EFEFEF', textAlign: 'center' }"
:border="true"
>
<el-table-column
type="selection"
width="55"
align="center"
v-if="showCheckBox"
></el-table-column>
<el-table-column label="序号" width="120" v-if="showNumber">
<template slot-scope="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column
v-for="(item, index) in headerList"
:key="index"
:prop="item.props"
:label="item.label"
:min-width="item.minWidth"
align="center"
></el-table-column>
<el-table-column label="操作" min-width="300" align="center">
<template slot-scope="scope">
<!-- 事项操作插槽 -->
<slot name="matterOperation" :row="scope.row"></slot>
<!-- 场景操作插槽 -->
<slot name="sceneOperation" :row="scope.row"></slot>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { log } from "console";
export default {
name: "Table",
props: {
headerList: {
type: Array,
},
tableList: {
type: Array,
},
showCheckBox: {
// 是否显示多选框
type: Boolean,
default: false,
},
showNumber: {
// 是否显示序号
type: Boolean,
default: true,
},
content: {
type: String,
default: "删除",
},
loading: {
// 是否显示 Loading 加载
type: Boolean,
default: false,
},
},
data() {
return {
listItem: "",
};
},
methods: {
tabRowClick(item) {
console.log(item, "item");
this.$emit("tabRowClick", item);
this.listItem = item;
},
},
};
</script>
<style lang="scss" scoped>
.cell button {
border: none;
padding-left: 0;
}
</style>
使用 :
<template>
<div>
<public-table
:tableList="tableList"
:showNumber="formData.showNumber"
:headerList="headerList"
>
<!-- 场景操作插槽 -->
<template #sceneOperation>
<el-tooltip content="占位">
<el-button
size="small"
icon="el-icon-edit-outline"
></el-button>
</el-tooltip>
</template>
</public-table>
</div>
</template>
<script>
import publicTable from '@/components/publicTable/index'
export default {
components: {
publicTable, // 公共表格组件
}
}
</script>