3-插槽

157 阅读2分钟

插槽分为:匿名插槽具名插槽作用域插槽

组件是可复用的,但是不可能所有地方使用某个组件时都是相同的,比如说按钮,现在希望登录按钮中包含一个a标签,注册按钮就是单纯的一个按钮,但它们使用同一个按钮组件来实现。这个时候就需要用到插槽,在 template模板中通过slot标签留槽,后期在使用组件时就可以填入自己想要的内容。

匿名插槽

定义一个全局的button组件MBtn,在template模板中加上<slot></slot>留槽,使用时往该组件中写入想要的内容会自动插入到slot这个槽中。

注意:该组件的命名在使用时可以直接写<MBtn></MBtn>,也可以<m-btn></m-btn>这样写。

<body>
    <div id="app">
        <App></App>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('MBtn', {
            template: `
            <button>
                <slot></slot>
            </button>
            `
        })

        const App = {
            template: `
            <div>
                <m-btn><a href="#">登录</a></m-btn>
                <m-btn>注册</m-btn>
            </div>
            `
        }

        new Vue({
            el: '#app',
            components: {
                App
            }
        })
    </script>
</body>

具名插槽

具名插槽就是在通过slot标签留槽的时候给它一个name属性命名用于区分。但是在使用的时候稍有不同,需要在组件标签中嵌套一个<template></template>,同时给它一个slot属性,该属性中填写的就是希望匹配的<slot></slot>name名。

注意:最后按钮显示的结果顺序与MBtn组件的插槽顺序无关,取决于App模板中的填充顺序,最先是提交按钮,然后是登录按钮,最后是注册按钮,在浏览器上也是这个显示顺序,而这个顺序与插槽的定义顺序并不一致。

<body>
    <div id="app">
        <App></App>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('MBtn', {
            template: `
            <button>
                <slot name='login'></slot>
                <slot name='register'></slot>
                <slot name='submit'></slot>
            </button>
            `
        })

        const App = {
            template: `
            <div>
                <m-btn>
                    <template slot='submit'>
                        提交
                    </template>
                </m-btn>
                <m-btn>
                    <template slot='login'>
                        登录
                    </template>
                </m-btn>
                <m-btn>
                    <template slot='register'>
                        注册
                    </template>
                </m-btn>
            </div>
            `
        }

        new Vue({
            el: '#app',
            components: {
                App
            }
        })
    </script>
</body>

作用域插槽

场景:已经开发了一个待办事项列表组件,很多模块都在使用。 要求:

  1. 之前数据格式和引用接口不变,正常显示
  2. 新功能模块增加对勾(已完成的事项打✔)

首先在todolist的模板中添加<slot></slot>插槽,并在其上绑定需要传回给父组件的数据(这些数据会封装成一个对象),然后在父组件中通过v-slot来接收。

<body>
    <div id="app">
        <App></App>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        const todolist = {
            props: {
                //接收一个数组
                todos: Array,
                //默认空数组
                defaultValue: []
            },
            //在slot上将当前每一项的数据绑定,在父组件中通过v-slot接收
            template: `
            <ul>
                <li v-for='item in todos' :key='item.id'>
                    <slot :itemValue='item'>
                    </slot>
                    {{item.title}}
                </li>
            </ul>
            `
        }

        const App = {
            data() {
                return {
                    todoList: [{
                        id: 1,
                        title: `散步`,
                        isComplete: true
                    }, {
                        id: 2,
                        title: '打电话',
                        isComplete: true
                    }, {
                        id: 3,
                        title: '玩游戏',
                        isComplete: false
                    }, {
                        id: 4,
                        title: '洗澡',
                        isComplete: true
                    }, {
                        id: 5,
                        title: '睡觉',
                        isComplete: false
                    }]
                }
            },
            components: {
                todolist
            },
            template: `
            <todolist :todos='todoList'>
                <template v-slot='data'>
                    <input type='checkbox' v-model='data.itemValue.isComplete'>
                </template>
            </todolist>
            `
        }

        new Vue({
            el: '#app',
            components: {
                App
            }
        })
    </script>
</body>