Vue 插槽

Vue 插槽

什么是插槽

slot翻译为插槽,在生活中有很多地方都有插槽,电脑的USB插槽,插板当中的电源插槽。

插槽的目的是让我们原来的设备具有更多的拓展性。

比如电脑的USB,我们可以接入U盘、硬盘、手机、音响、键盘、鼠标等等。

组件的插槽

组件的插槽也是为了让我们封装的组件更加具有拓展性。

让使用者可以决定组件内部的一些内容到底展示什么。

插槽就是在组件中预留的一个空间。可以自行选择在组件实例中插入哪些内容。

例如存在如下需求:

1、在页面中引入一组件,创建十个实例。每个实例都由自己的“个性”,1组件中使用a标签,2使用button,3使用input。。。

2、移动网站中的导航栏,封装为一个导航栏,在导航栏中预留若干个位置,预留五个插槽,在插槽中放入具体的实现。

如何封装这类组件?

抽取共性、保留不同

最好的封装方式就是将共性抽取到组件中,将不同暴露为插槽。

一旦我们预留了插槽,就可以让使用者根据自己的需求。决定插槽中插入什么内容。

是搜索框、是文字、还是菜单,由调用者自己来决定。

slot插槽

在子组件中,预留一对标签,就可以在父组件中创建子组件时,在其内填充自定义内容。

子组件:

<template>
    <div name='chidren'>
        <h1 v-text="title"></h1>
        <p v-text="info"></p>
        <input type="text" v-model="dparam" @input="valChange">
        <button @click="giao">发射属性到父组件</button>
        <button @click="btnClick">在子组件中获取父组件</button>
        <slot></slot>
    </div>
</template>

slot中也可以添加默认值。添加了默认值以后,即使没有在组件的内部添加新标签,也会默认加上默认值。

添加默认值:

<template>
    <div name='chidren'>
        <h1 v-text="title"></h1>
        <p v-text="info"></p>
        <input type="text" v-model="dparam" @input="valChange">
        <button @click="giao">发射属性到父组件</button>
        <button @click="btnClick">在子组件中获取父组件</button>
        <slot><button>我是默认值</button></slot>
    </div>
</template>

如果添加了默认值,但是父组件内进行了填充,那么默认值就不会生效。

父组件:

<template>
    <div name="parent">
        <h1 v-text="title"></h1>
        <input type="text" v-model="title">
        <button @click="btnClick">访问子组件中的方法</button>
        <Childred ref="child" @giao="giao" @title-change="titleChange" :title="title" :info="info">
            <h2>插槽中的具体内容</h2>
        </Childred>
    </div>
</template>

如果在一个插槽中,添加多个标签,那么这几个标签都会显示。

具名插槽<2.6+已废除>

如果在一个组件内,需要预留多个插槽,但是预留多个插槽在父组件内添加元素时,默认会将三个插槽的都替换。这就需要预留插槽的名称,在父组件内创建子组件实例的时候。指定其名称。直接指定slot的name属性即可。在替换时,需要指定slot="name"

parent:

<template>
    <div name='chidren'>
        <h1 v-text="title"></h1>
        <p v-text="info"></p>
        <input type="text" v-model="dparam" @input="valChange">
        <button @click="giao">发射属性到父组件</button>
        <button @click="btnClick">在子组件中获取父组件</button>
        <slot name="aa"><button>我是默认值</button></slot>
        <slot name="bb"><button>我是默认值</button></slot>
        <slot name="cc"><button>我是默认值</button></slot>
    </div>
</template>

childred:

<template>
    <div name="parent">
        <h1 v-text="title"></h1>
        <input type="text" v-model="title">
        <button @click="btnClick">访问子组件中的方法</button>
        <Childred ref="child" @giao="giao" @title-change="titleChange" :title="title" :info="info">
            <h2 slot="bb">插槽中的具体内容</h2>
        </Childred>
    </div>
</template>

具名插槽2.6+ 版本推荐写法

子组件的定义没有变化,父组件的引入修改为了以下的形式:

//组件调用时
<MyFooter v-red :age.sync="age">
  <template v-slot:footer>
  //这里v-slot:后边的值与组件内的slot的name属性对应,也就是插槽的名称。
      <div>list</div>
  </template>
</MyFooter>
//书写组件时
<template>
    <div>
        {{age}}
        <div>
            <slot name='footer' />
            //这里name的值就是这个插槽的名称。
        </div>
    </div>
</template>

即,在组件内部创建一个template标签,使用v-slot:name,代替原来的slot="name"

作用域插槽

作用域的介绍

真正的学习作用域插槽之前,我们需要先了解一个概念:编译作用域

Vue官方的解释:父组件模板的所有数据都会在父级作用域内编译,子组件模板的所有东西都会在子组件作用域内编译。

我理解的作用域就是 当父组件中使用子组件实例时,子组件中定义的data和父组件中的data的生效范围。何时会使用组件内部的data,何时会使用父组件中的data。

子组件:

<template>
    <div name='chidren'>
        <h1 v-text="title"></h1>
        <p v-text="info"></p>
        <input type="text" v-model="dparam" @input="valChange">
        <button @click="giao">发射属性到父组件</button>
        <button @click="btnClick">在子组件中获取父组件</button>
        <slot name="aa"><button>我是默认值aa</button></slot>
        <slot name="bb"><button>我是默认值bb</button></slot>
        <slot v-show="isShow" name="cc"><button>我是默认值cc</button></slot>
    </div>
</template>

<script>
export default {
    name: 'chidren',
    props: {
        title: String,
        info: String
    },
    data(){
        return{
            param: 'GIAO',
            dparam: this.title,
            isShow: false
        }
    },
    methods:{
        showInfo: function(){
            alert(this.param);
        },
        giao: function(){
            this.$emit('giao',this.param);
        },
        valChange: function(event){
            let newVal = event.target.value;
            this.param = newVal;
            this.$emit('title-change',newVal);
        },
        btnClick: function(){
            console.log(this.$parent.title);
        }
    }/*,
    watch:{
        dparam(oldVal,newVal){
            this.param = newVal;
            this.$emit('title-change',newVal);
        }
    }*/
}
</script>

<style>

</style>

父组件:

<template>
    <div name="parent">
        <h1 v-text="title"></h1>
        <input type="text" v-model="title">
        <button @click="btnClick">访问子组件中的方法</button>
        <Childred v-show="isShow" ref="child" @giao="giao" @title-change="titleChange" :title="title" :info="info">
            <h2 slot="bb">插槽中的具体内容</h2>
        </Childred>
    </div>
</template>

<script>
import Childred from './Chidren';
export default {
    name: 'parent',
    data(){
        return {
            title: 'rayfoo',
            info: '真帅',
            isShow: true
        }
    },
    methods:{
        giao: function(param){
            alert(param);
        },
        titleChange: function(newVal){
            this.title = newVal;
        },
        btnClick: function(){
            console.log(this.$children[0].param);
            this.$children[0].showInfo();
            console.log(this.$refs.child.param);
        }
    },
    components:{
        Childred
    }
}
</script>

<style>

</style>

此时,子组件和父组件中都有isShow,但是它们的作用范围却是不一样的

2.6+中使用作用域插槽

作用域插槽是slot中一个比较难以理解的点,且官方文档说的又有点不清晰。

这里,我们用一句话对其做一个总结,然后我们在后续的案例中来体会:

  • 父组件替换插槽的标签,但是内容由子组件来提供

需求:

  • 子组件中包括一组数据,比如clan['js','py','go']
  • 需要在多个界面中进行展示:
    • 某些界面以水平方向展示
    • 某些页面是以列表形式的
    • 某些是直接展示一个数组
  • 内容在子组件中,但是希望父组件告诉我们如何去展示,该怎么办呢?
    • 使用slot的作用域插槽即可。
//组件调用
 <ul>
   <myli :title="val.title"
   >
   <template v-slot:footer="message">
       <div>{{message.aa}}</div>
   </template>
   </myli>
 </ul>
 //书写组件时
 <template>
    <li>
        <slot name='footer' :aa="title">
        </slot>
    </li>
</template>

此处的message是一个自定义的名称,其保存slot中的属性集合,通过message可以调用slot中的所有属性,简化了slot-scope的操作

一句话概括就是v-slot :后边是插槽名称,=后边是组件内部绑定作用域值的映射。

另一个具名插槽+作用域插槽的案例

子组件:

    data(){
        return{
            hobby: ['抽烟','喝酒','烫头']
        }
    }
    <slot :hobby="hobby" name="cc"><button>我是默认值cc</button></slot>

父组件:

        <Childred >
            <template v-slot:cc="prop">
                <ul  v-for="(item,index) in prop.hobby" :key=index>
                    <li v-text="item"></li>
                </ul>
            </template>
        </Childred>
原文地址:https://www.cnblogs.com/zhangruifeng/p/13539630.html