Vue.js作用域插槽的理解和应用

注意:Vue自 2.6.0 起已废弃的使用 slot-scope 特性的语法,因此下文内容已经过时,待更新。

Vue.js的官方文档很好,基本上看完就能上手,但是作用域插槽一节却略显模糊。并没有用准确的语言描述,而是直接给出例子,对于开发经验还不丰富的新手们,可能不知道有什么实际应用场景。

什么是“作用域插槽”?

官方文档的描述是:

有的时候你希望提供的组件带有一个可从子组件获取数据的可复用的插槽。

“组件”和“插槽”

很明显,在此之上你必须知道什么是“组件”和“插槽”。
父组件通过 Prop 向子组件传递数据,通过插槽向子组件传递内容。它们的共同点都是从上(父)往下(子)传递数据,这里也稍微讲下它们的区别:Prop传递的是组件的属性,而插槽是传递VNode节点。

作用域、插槽

那作用域插槽呢?它是跟上面两者传输数据的方向相反,是下(子)往上(父)传递数据,我们再回头看看官方的描述:“可从子组件获取数据”,正是说明这种关系。

那么它跟插槽有什么关系呢?因为数据像是“绑定”在子组件的插槽上,哪个VNode节点插入到该插槽上,那个节点能拿到子组件传递上来的数据,那么slot-scope属性指定的值,就是我们用于接收数据的变量。

下面使用简单的代码来描述这种关系,请留意下面的注释,并按注释序号顺序阅读。

代码1:组件Child

<template>
   <div id="child">
       <!-- 2、但是通过 v-bind 把内部数据“绑定”到插槽上。当然,其实上是绑定到未来插入到该插槽的节点上。 -->
       <slot v-bind="slotData"/>
   </div>
</template>

<script>
export default {
   name: 'Child',
   data() {
       return {
           // 1、这里 slotData 是子组件内部数据
           slotData: { msg: '我是子组件数据' }
       };
   }
};
</script>

现在有一张页面,需要使用组件Child,那么这个页面的角色当然就是父组件:

代码2:父组件

<div id="page">
   <!-- 1、使用Child组件,通过默认插槽传入一个<h1>节点 -->
   <!-- 2、通过slot-scope指定一个变量 abc 来接收子组件传上来的数据 -->
   <Child>
       <h1 slot-scope="abc">{{ abc.msg }}</h1>
   </Child>
</div>

代码3:最终输出

<div id="page">
    <div id="child">
        <h1>我是子组件数据</h1>
    </div>
</div>

通过上面例子可以看出,代码2里面的abc变量,其实就是代码2定义的slotData对象。slot-scope="abc"的作用可以理解为:const abc = slotData,至于解构slot-scope,对应上面例子当然就是const { msg } = slotData;

什么情况下会用到作用域插槽?

说到子组件往父组件传递数据,Vue.js 文档不是明确写了“通过事件向父级组件发送消息”吗?或者通过Vuex,更加自由。那么我们为什么还需要作用域插槽呢?

它们作用确实有很多相似性,自定义事件已经适用于大多数场景,但是作用域插槽在下面场景能提供不少便利:
组件在只有自己的子组件会使用自己提供的数据时,通常出现在遍历渲染时。

一个页面(当然页面也是个组件,叫组件Page)有组件A包含组件B。

代码4:布局

<A><B /></A>

组件B依赖组件A过运算之后传递回来的数据,并且没有其他程序需要这个数据。

如果通过自定义事件event-a推送结果,那么我们必须在组件A上监听事件,并在页面Page的data上定义一个变量aa

代码5:使用事件

<template>
    <A v-on:event-a="setA">
        <B v-bind="aa"/>
    </A>
</template>

<script>
export default {
    name: 'Page',
    data() {
        return {
            aa: {}
        };
    },
    methods: {
        setA(aa) {
            this.aa = aa;
        }
    }
};
</script>

如果组件A使用作用域插槽传回B需要的数据呢?

代码6:使用作用域插槽

<template>
    <A>
        <B slot-scope="aa" v-bind="aa"/>
    </A>
</template>

<script>
export default {
    name: 'Page'
};
</script>

是不是简单很多?当然代码5为了方便新手而写累赘了,但是代码6少定义一个函数,并且变量 aa跟像是函数内部用完即丢的变量,代码5的aa就一直跟随页面存在。当你需要遍历的时候组件A的时候,后者的优势更加明显

后记

之前写的内容感觉过时了,于2018-12-27重写,希望能帮到大家。

推荐阅读更多精彩内容

  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 2,657评论 0 30
  • 此文基于官方文档,里面部分例子有改动,加上了一些自己的理解 什么是组件? 组件(Component)是 Vue.j...
    陆志均阅读 1,766评论 5 15
  • 以下内容是我在学习和研究Vue时,对Vue的特性、重点和注意事项的提取、精练和总结,可以做为Vue特性的字典; 1...
    科研者阅读 10,209评论 3 21
  • 下载安装搭建环境 可以选npm安装,或者简单下载一个开发版的vue.js文件 浏览器打开加载有vue的文档时,控制...
    冥冥2017阅读 3,415评论 0 43
  • 9.1 什么是组件? 组件(Component)是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,...
    白水螺丝阅读 254评论 0 2