vue插槽

1、编译作用域

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

  • 官方对于编译的作用域解析比较简单,我们自己来通过一个例子来理解这个概念:

  • 我们来考虑下面的代码是否最终是可以渲染出来的:

  • <my-cpn v-show="isShow"></my-cpn>中,我们使用了isShow属性。

  • isShow属性包含在组件中,也包含在Vue实例中。

  • 答案:最终可以渲染出来,也就是使用的是Vue实例的属性。

  • 为什么呢?

  • 官方给出了一条准则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译。

  • 而我们在使用<my-cpn v-show="isShow"></my-cpn>的时候,整个组件的使用过程是相当于在父组件中出现的。

  • 那么他的作用域就是父组件,使用的属性也是属于父组件的属性。

  • 因此,isShow使用的是Vue实例中的属性,而不是子组件的属性。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    //显示,用的是vue父组件的作用域
    <cpn v-show="isShow"></cpn>
</div>

<template id="cpn">
    <div>
        <h2>我是子组件</h2>
        <p>我是内容, 哈哈哈</p>
        //不会显示,用的是子组件的作用域
        <button v-show="isShow">按钮</button>
    </div>
</template>

<script src="../../js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            message: '你好啊',
            isShow: true
        },
        components: {
            cpn: {
                template: '#cpn',
                data() {
                    return {
                        isShow: false
                    }
                }
            }
        }
    })
</script>

</body>
</html>

2、具名插槽slot

  • 当子组件的功能复杂时,子组件的插槽可能并非是一个。

  • 比如我们封装一个导航栏的子组件,可能就需要三个插槽,分别代表左边、中间、右边。

  • 那么,外面在给插槽插入内容时,如何区分插入的是哪一个呢?

  • 这个时候,我们就需要给插槽起一个名字

  • 如何使用具名插槽呢?

  • 非常简单,只要给slot元素一个name属性即可

  • <slot name='myslot'></slot>

  • 我们来给出一个案例:

  • 这里我们先不对导航组件做非常复杂的封装,先了解具名插槽的用法。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app">

    <!--这个只会替换模板里没有定义name属性的插槽,如果要替换有name的插槽,指定name的值即可-->
    <cpn><span>我是cpan,替换没有定义name属性的插槽</span></cpn>
    <!--替换left的插槽的值-->
    <cpn><span slog="left">替换左边的插槽的值</span></cpn>

</div>
<template id="cpn">
    <div>
        <slot name="left"><span>左边</span></slot>
        <slot name="center"><span>中间</span></slot>
        <slot name="right"><span>右边</span></slot>
        <slot><span>我没有name</span></slot>
    </div>
</template>
</body>
<script src="../../js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            message: '你好啊'
        },
        components: {
            cpn: {
                template: '#cpn'
            }
        }
    })
</script>
</html>

3、作用域插槽

在父组件使用我们的子组件时,从子组件中拿到数据:

  • 我们通过<template slot-scope="slotProps">获取到slotProps属性,slotProps可以随便命名

  • 在通过slotProps.data就可以获取到刚才我们传入的data了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <cpn></cpn>

    <cpn>
        <!--目的是获取子组件中的pLanguages,2.5版本一下,必须用template,版本以上可使用其他标签,比如div-->
        <template slot-scope="slottemp">
            <!--<span v-for="item in slottemp.data"> - {{item}}</span>-->
            <span>{{slottemp.data.join(' - ')}}</span>
        </template>
    </cpn>

    <cpn>
        <!--目的是获取子组件中的pLanguages-->
        <template slot-scope="slottemp">
            <!--<span v-for="item in slot.data">{{item}} * </span>-->
            <span>{{slottemp.data.join(' * ')}}</span>
        </template>
    </cpn>
</div>
<template id="cpn">
    <div>
        <slot :data="pLanguages">
            <ul>
                <li v-for="item in pLanguages">{{item}}</li>
            </ul>
        </slot>
    </div>
</template>
<script src="../../js/vue.js"></script>
<script>
    const app = new Vue({
        el: '#app',
        data: {
            message: '你好啊'
        },
        components: {
            cpn: {
                template: '#cpn',
                data() {
                    return {
                        pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
                    }
                }
            }
        }
    })
</script>


</body>
</html>