Vue.js:做用域插槽:得到子组件中的数据

组件的Vue做用域的隔离

<current-user>
  {{ user.firstName }}
</current-user>
<!-- current-user这一组件的内部实现 -->
<span>
  <slot>
    {{ user.lastName }}
  </slot>
</span>

这两段代码,就已经有了两个不一样的Vue做用域,一个是 使用current-user模块 的页面或模块所具备的Vue实例与对应的做用域,另外一个则是current-user这一模块自身对应的Vue实例与对应做用域。html

这两个做用域彼此互相隔离,再不加任何额外设置的状况下,没法访问到彼此Vue实例,所以不要说什么datamethods之类的,Vue实例所具备的成员通通都是没法交互的。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实例交互彼此的信息,那么有两种状况:第一,父知晓子的信息;第二,子知晓父的信息。segmentfault

做用域插槽的真正目的是让父知晓子的信息

一开始误觉得做用域插槽的目的是让子能知晓父的信息,让我始终没法理解做用域插槽。但若是带着做用域插槽的目的是让父知子这一想法去看,就以为官方文档豁然开朗了。官方文档传送门数组

截图1

图中的user从何而来?这么来的ide

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须要子告知父!测试

映射关系图

通过解析这幅图上的映射关系,咱们就能明白父为什么可以知晓子中的信息。ui

首先,父在使用组件时,不能像原来同样舒舒服服地直接写上想要替换掉<slot>的内容,而是要先用一个<template>spa

其次,父亲必需要知道想要了解的子的名字,若是只有一个孩子,那这个孩子确定叫'defualt',此时在子中不写明无所谓,在父中不写明也无所谓。即图中的name="default"v-slot:default="slotProps":defualt都可删去。3d

而后,父亲想要知道子中的数据,子必需孝顺(开发者必需事先考虑到)而准备好一个slot标签并带上v-bind:[key]="dataOfMine"属性。code

如今,如前述所言,孝顺的子考虑到父亲须要知道本身的数据而作好了准备——而在本身的模块中写一个<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来记录本身是第几个,此时咱们想在父中访问这个数据,就要用到做用域插槽了。

相关文章
相关标签/搜索