组件的Vue作用域的隔离
<current-user>
{{ user.firstName }}
</current-user>
<!-- current-user这一组件的内部实现 -->
<span>
<slot>
{{ user.lastName }}
</slot>
</span>
这两段代码,就已经有了两个不同的Vue作用域,一个是 使用current-user
模块 的页面或模块所具有的Vue实例与对应的作用域,另一个则是current-user
这一模块自身对应的Vue实例与对应作用域。
这两个作用域彼此互相隔离,再不加任何额外设置的情况下,无法访问到彼此Vue实例,因此不要说什么data
,methods
之类的,Vue实例所具有的成员统统都是无法交互的。
正是在此基础上,当我们需要达成这两个作用域能彼此知晓一些信息的目标时,不得不采取一些设置。这设置里就包括了作用域插槽。同时,这两个作用域可以认为存在父子关系,即第一段代码中使用current-user
模块的页面或模块(比如说,第一段代码是一个index.html
的节选。)的Vue作用域是第二段代码的被调用的current-user
模块(比如说,这段代码是一个currentuser.vue
文件的节选)的Vue作用域的父作用域(index.html
引入了currentuser.vue
从而使用<curent-user></curent-user>
。在这篇文章里,称index.html
的Vue实例是currentuser.vue
的Vue实例的父亲)。基于此,当我们想要让两个不能轻易交互但有使用关系的Vue实例交互彼此的信息,那么有两种情况:第一,父知晓子的信息;第二,子知晓父的信息。
作用域插槽的真正目的是让父知晓子的信息
一开始误以为作用域插槽的目的是让子能知晓父的信息,让我始终无法理解作用域插槽。但如果带着作用域插槽的目的是让父知子这一想法去看,就觉得官方文档豁然开朗了。官方文档传送门。
图中的user从何而来?这么来的
current-user.vue
<template>
<span>
<slot v-bind:user="user">
{{ user.lastName }}
</slot>
</span>
</template>
<script>
export default {
data(){
return {
user:{
firstName:"张",
lastName:"飞",
}
}
}
}
</script>
<style></style>
是的,这个user
打一开始就是在current-user
这个模块(current-user.vue
这个文件)内部的Vue作用域(想想我们之前说的父和子,也就是说user
数据在子模块中)中,官方文档这一章节想要解决的不是模块内部的{{user.lastName}}
的user
需要父传入,而是父中{{ user.firstName }}
的user
需要子告知父!
经过解析这幅图上的映射关系,我们就能明白父为何能够知晓子中的信息。
首先,父在使用组件时,不能像原来一样舒舒服服地直接写上想要替换掉<slot>
的内容,而是要先用一个<template>
其次,父亲必须要知道想要了解的子的名字,如果只有一个孩子,那这个孩子肯定叫'defualt',此时在子中不写明无所谓,在父中不写明也无所谓。即图中的name="default"
和v-slot:default="slotProps"
的:defualt
均可删去。
然后,父亲想要知道子中的数据,子必需孝顺(开发者必需事先考虑到)而准备好一个slot
标签并带上v-bind:[key]="dataOfMine"
属性。
现在,如前述所言,孝顺的子考虑到父亲需要知道自己的数据而做好了准备——而在自己的模块中写一个<slot></slot>
并为它带上v-bind:[key]="dataOfMine"
属性。对每一个带v-bind:[key]="dataOfMine"
属性的<slot>
元素。根据父亲的<template></template>
中的属性v-slot:childname="propertiesSentByChild"
,子告知父的信息(通过v-bind:[key]="dataOfMine"
属性的<slot>
元素告知的,至于怎么回事儿你继续往下看。)会被装入一个名为propertiesSentByChild
的父Vue实例作用域内可访问的对象(或者干脆理解成Map好了,毕竟最典型的行为是通过键得到值。),这个对象通过propertiesSentByChild
来引用,通过propertiesSentByChild。key
(对没错,这里的key就是上面几行那个v-bind:[key]="dataOfMine"
里的key)得到dataOfMine
(对没错,这里的key就是上面几行那个v-bind:[key]="dataOfMine"
里的dataOfMine),而这个dataOfMine
就是子Vue实例中的数据。比如这里把dataOfMine
写成user
的话,那么那个firstname为张,lastname为飞的对象就在父中被propertiesSentByChildkey.key
得到啦,如果你console.log(propertiesSentByChild.key.firstName)
,你还能得到“张”的输出呢。另外这里的propertiesSentByChild
也是任由开发者自己写的,只要前后对应一致即可。
到此,作用域插槽的真正目的和写法已经讲明白了。接下来让我们看看它的实际使用。
作用域插槽的实际使用
ElementUI的table组件的”自定义列模板“样式中操作列的按钮使用了作用域插槽
这里就只贴有关部分代码了
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
这里使用的时已经废弃的写法,贴一下废弃写法转换为目前推荐写法的对应关系
还是老样子,如果只有一个<slot>
则 default 写不写无所谓。
那么在这个ElementUI的实例中,可以看到它对作用域插槽的使用:scope.$index
,这说明$index
是<el-table-column>
模块内部的一个数据,经过console.log()
大法测试,发现这个对应的就是被点击删除的按钮所在行的行数减一。
这样一想还挺合理的,<el-table-column>
中的数据遍历<el-table>
的:data=
绑定的数组而不断生成,每次生成,其内部(也就是子)都存在一个序号$index
来记录自己是第几个,此时我们想在父中访问这个数据,就要用到作用域插槽了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。