前两种很好理解,无论就是子组件里定义一个slot占位符,父组件调用时,在slot对应的位置填充模板就好了。重点讲第三种,作用域插槽。
作用域插槽的慨念,文档却只有一句简单的描述
有的时候你希望提供的组件带有一个可从子组件获取数据的可复用的插槽。
语言描述太抽象,我想象不到slot-scope的使用场景。
介绍了写这篇文章的来由,接下来简述一下本文的脉络
下面是2个父子的vue组件,先解释一下2个组件做了什么事情
我建议从数据流动的角度,理解插槽作用域的使用方式,(先学会怎么用,暂时不用理解为什么要这么用,使用场景是第二部分)
所以数据的流动经历了
我贴出全部代码,方便大家自己研究
父组件的源码,也就是调用者
<template>
<todo-list :todos="todos">
<template slot-scope="slotProps">
<span v-if="slotProps.todo.isComplete">✓</span>
<span>{{slotProps.todo.text}}</span>
</template>
</todo-list>
</template>
<script>
import todoList from './todoList'
export default {
data () {
return {
todos: [
{
id: 0,
text: 'ziwei0',
isComplete: false
},
{
text: 'ziwei1',
id: 1,
isComplete: true
},
{
text: 'ziwei2',
id: 2,
isComplete: false
},
{
text: 'ziwei3',
id: 3,
isComplete: false
}
]
}
},
components: {
todoList
},
}
</script>
子组件源码,也就是封装组件的人
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
<slot :todo="todo">
</slot>
</li>
</ul>
</template>
<script>
export default {
props: {
todos: {
type: Array
}
}
}
</script>
想象一个场景:
当你要给同事封装一个列表组件,你就需要使用作用域插槽(注意是列表或者类似列表的组件)
你开发的这个列表组件要如何使用呢?
一般来说作为列表组件的调用者,你的同事先做ajax请求,拿到一个这样的数组
todos: [
{
id: 0,
text: 'ziwei0',
isComplete: false
},
{
text: 'ziwei1',
id: 1,
isComplete: true
},
{
text: 'ziwei2',
id: 2,
isComplete: false
},
{
text: 'ziwei3',
id: 3,
isComplete: false
}
]
之后会把todso传递给列表组件吧,那么列表组件内部做什么事情呢?
列表内部肯定会v-for去帮你的同事渲染这个数组嘛。 就类似element-ui里的table组件一样
问题的关键就在这里
列表组件的循环,是发生在组件内部的,所以通过 v-for=“todo in todos” ,列表组件很容易拿到每一项todo,但列表拿到数据没用呀,列表只是一个瓜皮,列表展示的布局和使用的参数是不固定的,所以你把数据交给子组件循环渲染是不行的,你需要在父组件来定义子组件的数据的展示效果。
那么你怎样才能把每一项的todo数据给传递出去呢?
你会发现没有办法!
无论是用$emit、vuex还是localStorage,可以考虑一下,会发现没有合适的时机,能让你把todo传递出去
所以为了应对这个场景下,发明了作用域插槽,列表组件可以通过< slot :todo=“todo”>传递todo出去
你的同事可以通过 slot-scope=“slotsProps”(v-slot=“slotProps”)拿到todo。
<template>
<todo-list :todos="todos">
<template slot-scope="slotProps"> // 就是这段代码
<span v-if="slotProps.todo.isComplete">✓</span>
<span>{{slotProps.todo.text}}</span>
</template>
</todo-list>
</template>
的确,调用ui组件时一般是我们传递配置参数给他们来进行数据的展示。
但是存在一种情况,你传递的参数是不固定的,比如elemnt-ui的table组件,你把数组传递给table后,设置prop参数对应的字段名称,来展示对应row对象中名称的值。
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"// <---就是这里
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</template>
因为循环的过程发生在table组件内部,所以table组件可以方便的获取到每一项数据,但是这些数据最终不是给组件的,而是我们自己要用的业务数据。所以也需要一个方式,让调用者能拿到自己想要的数据
如果你不把数据给子组件当然可以。但是就等于抛弃掉了子组件的封装,只能你直接在父组件自己写一个列表
毕竟你不把数据给子组件,子组件还渲染个锤子?没有父子关系的话,也就不用什么插槽了。
但是咱不是为了封装后,可以复用嘛,总不能永远不用组件嘛,每个页面都得从头写。
对我来说,就是同样的事情只做一次,之后就是ctrl+c,v,提高开发效率,只为了更好的摸鱼!
e m i t 和 v u e x 是数据传递的一种方法,但是你可以尝试用 emit和vuex是数据传递的一种方法,但是你可以尝试用 emit和vuex是数据传递的一种方法,但是你可以尝试用emit和vuex把todo传递给父组件。
你会发现的确没有合适的钩子、时机来$emit数据
我认为几种说法是不太恰当的,也是给我造成一些困惑的
这种说法,会让我觉得slot-scope跟emit和vuex是一类东西
因为真正的使用场景下,子组件的数据都是来自父组件的。作为组件内部应该保持纯净。
就像element-ui里的table组件,肯定不会定义一些数据在组件内部,然后传递给你。
table组件的数据都是来自调用者的,然后table会把每一行的row,在开发者需要时,传递出去。
这些例子虽然不是错误,但是我觉得反而不利于理解slot-scope
链接: 学习自链接