1. 为什么使用插槽?
组件的插槽是为了让我们封装的组件更具有扩展性,让使用者决定组件内部展示的内容是什么。
有些组件之间有些区别,但是也有很多的共性,如果我们每个都去封装一个组件,这似乎并不合适,但是如果封装成一个组件,也不合理,因为每个组件之间不完全一样,有些差别,所以我们可以用插槽来解决这些问题。
最好的封装方式就是抽取组件中的共性封装起来,将不同的地方定义为插槽暴露出去,这样既可以根据使用的需求,决定插槽内容是什么。
插槽的作用就是父组件向子组件传递内容:
2. 内容插槽
我们定义两个插槽:parent.vue
和child.vue
,在parent组件中引入child组件。
// parent.vue
<child>
world
</child>
//child.vue
<template>
<div>
hello <slot>你好</slot>
</div>
</template>
这样,组件在渲染的时候,<slot></slot>
就会被渲染为:hello world,如果在<child></child>
中没有放任何内容,就会渲染出子组件的默认内容:hello 你好
插槽的内部不仅可以是文本内容,还可以是HTML或者其他模板代码。
3. 具名插槽
有时候,我们在一个组件中需要使用多个插槽,那么为了区分它们,就可以给每个插槽精心命名,这就是具名插槽。
slot
元素有一个特殊的attribute:name
,他可以用来指定额外的插槽:
// 子组件
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
// 父组件
<base-layout>
<template v-slot:header>
<h1>header</h1>
</template>
<p>content</p>
<template v-slot:footer>
<p>footer</p>
</template>
</base-layout>
使用v-slot
指令来指定该部分要渲染的插槽的名字,这样就可以将三部分内容渲染到相应的位置了。不带name的插槽,会有一个默认的名字default
。如果想要更明确一些,可以将它的name设置为default:
<template v-slot:default>
<p> content </p>
</template>
需要注意的是,v-slot
指令只能添加在template
标签上。
4. 作用域插槽
作用域插槽对于我来说,可能比上面的两种插槽都难一点,但是也非常的有用,最近在联系中多次使用到了作用域插槽,这里就来详细整理一下。
一般情况下,是父组件使用查操过程中传入一些内容来决定插槽的内容,他可以访问到父组件中定义的属性,但是不能访问子组件中的数据。如果我们需要子组件以供一些数据,那么就可以使用作用域插槽来解决。
在子组件中动态绑定需要传入的数据:
<slot :data="data"></slot>
export default {
data () {
return {
data: {
username: 'hello'
}
}
}
}
在父组件中访问子元素的数据:
<div>
<test v-slot:default="slotProps">
{{slotProps.data.username}}
</test>
</div>
// 也可以写成下面这样
<div>
<test v-slot:"slotProps">
{{slotProps.data.username}}
</test>
</div>
需要注意: 如果有多个插槽,只要将default改为对应的插槽名称即可。
来看一下最近刚写的代码,用到了作用域插槽,但是形式和上面有所不同。这是用Element UI写的一个表格,这里用的还是slot-scope
,在Vue2.6
中已经取消了这种写法,由v-slot
指令来代替,改成v-slot
即可,在这里使用slot-scope
也能执行,就先不改了~
这个表格中,两次用到了作用域插槽,第一次是“创建时间”栏,通过作用与插槽可以获得所在作用域中的所有数据,他所在的作用于就是表格的这一行,然后选择所需要的添加时间的数据,就可以对其进行过滤操作了;
第二次使用是最后操作按钮一栏,想要对这一行数据进行操作,首先就需要获取这一样的数据,作用域插槽就可以完美实现,得到该行的数据,就可以顺利进行后面的操作。
<el-table :data="goodsList" border stripe>
<el-table-column type="index"></el-table-column>
<el-table-column label="商品名称" prop="goods_name"></el-table-column>
<el-table-column label="商品价格(元)" prop="goods_price" width="110px"></el-table-column>
<el-table-column label="商品重量" prop="goods_weight" width="70px"></el-table-column>
<el-table-column label="创建时间" width="140px">
<template slot-scope="scope">
{{scope.row.add_time | dataFormat}}
</template>
</el-table-column>
<el-table-column label="操作" width="130px">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" size="mini"></el-button>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="removeById(scope.row.goods_id)"></el-button>
</template>
</el-table-column>
</el-table>
插槽的解构
作用域插槽的内部工作原理是将插槽的内容包含在一个带有单个参数的函数里,所以slot 的值可以接收任何有效的可以出现在函数定义的参数位置上的 JavaScript 表达式。
原本这样写的代码:
<div>
<test v-slot:"slotProps">
{{slotProps.data.username}}
</test>
</div>
可以改成这样:
<div>
<test v-slot:{data}>
{{data.username}}
</test>
</div>
这样写代码就更加的简洁。
5. 动态插槽名
在Vue2.6中引入了动态插槽名,动态指令参数使用在v-slot上,来定义动态的插槽名:
<base-layout>
<template v-slot:[dynamicSlotName]>
...
</template>
</base-layout>
6. 具名插槽的缩写
在Vue2.56 中还新增了具名插槽的缩写,之前v-on
(缩写为@
),v-bind
(缩写为:)都可以缩写,在Vue2.6中,v-slot
可以缩写为#
:
<template v-slot:header>
<h1>header</h1>
</template>
// 缩写后
<template #header>
<h1>header</h1>
</template>
需要注意,该缩写需要在其有参数的情况下才能使用,下面的情况会弹出警告:
<test #:{data}>
{{data.username}}
</test>
如果非要使用缩写的话,必须给他指定一个插槽名:
<test #default:{data}>
{{data.username}}
</test>