插槽详解

本文详细介绍了Vue.js中的插槽机制,包括插槽的概念、使用方式、后备内容、具名插槽、作用域插槽、动态插槽名、具名插槽的缩写以及其他示例。插槽允许父组件向子组件注入内容,实现组件的灵活组合。通过v-slot统一了之前的slot和slot-scope,同时支持具名插槽和作用域插槽,提升了组件的复用性和可定制性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

插槽详解

在 2.6.0 版本之后,slot 以及 slot-scope 由 v-slot 统一替代,但依然未被移除。

1. 什么是插槽

插槽是 vue.js 开发员人根据从 Web Components 规范草案中获取的灵感,设计的一套 API,这套 API 允许我们对组件进行组合。

插槽就是子组件中的提供给父组件使用的一个占位符,用 表示,父组件可以在这个占位符中填充任何模板代码,如 HTML、组件等,填充的内容会替换子组件的<slot></slot>标签。

类似于 React.js 中的组合(children prop),插槽允许将一些其他的组件作为子组件传递给指定组件。

2. 如何使用

比如下面的代码:

在子组件中:

<template>
	<div>
        <h1>
  			插槽的使用
    	</h1>
        <slot></slot>
    </div>
</template>
<script>
export default{
    name: 'child'
}
</script>

在父组件中:

<template>
	<div>
     	<div>
            使用插槽分发内容
    	</div>
        <Child>
    		<div>
                这里的 div 替代了 slot 标签。
    		</div>
    	</Child>
    </div>
</template>
<script>
import Child from './components/Child';
export default {
    name: 'Father',
}
</script>

这样,就会出现:

在这里插入图片描述

如果,在子组件中,去除 <slot></slot>,那么则不会出现 这里的 div 替代了 slot 标签

总结:如果子组件没有使用插槽,父组件如果需要往子组件中填充模板或html,是无法做到的。

但是,使用插槽需要注意作用域,子模板里的所有内容都是在子作用域中编译的。父级模板里的所有内容都是在父级作用域中编译的。

比如,还是上面的例子,如果我们这样使用,在父组件中,有一个 props:

<template>
	<div>
     	<div>
            使用插槽分发内容
    	</div>
        <child>
    		<div>
                这里的 div 替代了 slot 标签。
    		</div>
    	</child>
    </div>
</template>
<script>
import child from './components/Child';
export default {
    name: 'Father',
    props: ['name'],
}
</script>

在 App.vue 中导入 Father 组件:

<template>
  <div id="app">
	<Father name="father" />
  </div>
</template>

<script>
import Father from '@/components/Father';
export default {
  name: 'App',
  components: {
    Father,
  }
}
</script>

<style scope>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  height: 100%;
}

</style>

那么,子组件 Child 是访问不到 name,这个 props 的。

3. 后备内容

后备内容,指挥在没有提供内容的时候被渲染。来看一下官网的例子:

<submit-button> 组件中:

<button type="submit">
    <slot></slot>
</button>

如果在 <slot></slot> 标签中,填入 Submit 内容,那么,当父组件没有向其中添加其他内容的时候,会默认渲染 Submit

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<body>
    <div id="app">
        <submit-button>   
        提交
        </submit-button>
    </div>
</body>
<script>
    Vue.component('submit-button', {
        template: '<button type="submit"><slot>Submit</slot></button>'
    })

    new Vue({
        el: '#app'
    })
</script>

</html>

结果:

在这里插入图片描述

如果在父组件中向插槽中插入了其他的内容:

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<body>
    <div id="app">
        <submit-button>   
        提交
        </submit-button>
        <father-button />
    </div>
</body>
<script>
    Vue.component('submit-button', {
        template: '<button type="submit"><slot>Submit</slot></button>'
    })

    Vue.component('father-button', {
        template: '<div><div>添加了插槽内容</div><submit-button><span>订阅</span></submit-button></div>',
    });

    new Vue({
        el: '#app'
    })
</script>

</html>

则:
在这里插入图片描述

此时,多出了:

在这里插入图片描述

4. 具名插槽

描述:具名插槽其实就是给插槽去一个名字。这就意味着,一个子组件可以放多个插槽,而且可以放在不同的地方,而父组件填充内容时,可以根据插槽的名字把内容填充到对应的插槽中。

官网有这样的例子:

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <base-layout>
            <template v-slot:header>
                <h1>Here might be apage title</h1>
            </template>

            <template v-slot:default>
                <p>a paragraph for the main content</p>
                <p>And another one.</p>
            </template>

            <template v-slot:footer>
                <p>Here's some contact info</p>
            </template>
        </base-layout>
    </div>
</body>
<script>
    Vue.component('base-layout', {
        template: '<div class="container">'+
                    '<header>'+
                        '<slot name="header"></slot>'+
                    '</header>' +
                    '<main>' +
                        '<slot></slot>' +
                    '</main>' +
                    '<footer>' +
                        '<slot name="footer"></slot>' +
                    '</footer>' +
                '</div>' 
    })

    new Vue({
        el: '#app'
    })
</script>

</html>

子组件可以根据 <slot></slot> 标签中特殊的属性 attribute 来确定每个插槽唯一的名称,而如果出厂时并没有设置 name 属性,那么,name 属性会默认为 default

然后,父组件可以通过 <template> 标签上的 v-slot 属性,根据 name 属性不同的属性值,来选择性地为子组件中的插槽添加内容。

当然,子组件中的每个插槽都可以明确自己的后备内容。

需要注意的是,所有没有 name 属性的 <slot></slot>,都会被默认为拥有 default 属性值的 <slot></slot>

如:

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <base-layout>
            <template v-slot:header>
                <h1>Here might be apage title</h1>
            </template>

            <template v-slot:default>
                <p>a paragraph for the main content</p>
                <p>And another one.</p>
            </template>

            <template v-slot:footer>
                <p>Here's some contact info</p>
            </template>
        </base-layout>
        <hr />
        <base-layout-again>
            <template v-slot:header>
                <h1>Here might be apage title</h1>
            </template>

            <template v-slot:default>
                <p>a paragraph for the main content</p>
                <p>And another one.</p>
            </template>

        </base-layout-again>
    </div>
</body>
<script>
    Vue.component('base-layout', {
        template: '<div class="container">'+
                    '<header>'+
                        '<slot name="header"></slot>'+
                    '</header>' +
                    '<main>' +
                        '<slot></slot>' +
                    '</main>' +
                    '<footer>' +
                        '<slot name="footer"></slot>' +
                    '</footer>' +
                '</div>' 
    })

    Vue.component('base-layout-again', {
        template: '<div class="container">'+
                    '<header>'+
                        '<slot name="header"></slot>'+
                    '</header>' +
                    '<main>' +
                        '<slot></slot>' +
                    '</main>' +
                    '<footer>' +
                        '<slot></slot>' +
                    '</footer>' +
                '</div>' 
    })

    new Vue({
        el: '#app'
    })
</script>

</html>

此时:

在这里插入图片描述

当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。

如:

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <own-default v-slot="slotProps">
            {{ slotProps.user.firstname }}
        </own-default>
    </div>
</body>
<script>


    Vue.component('own-default', {
        template: '<div class="container">'+
                    '<slot :user="user"></slot>' +
                '</div>' ,
        data: () => {
            return {
                user: {
                    firstname: 'qian',
                    lastname: 'qian',
                }
            }
        }
    });

    new Vue({
        el: '#app'
    });
</script>

</html>

在这里插入图片描述

注意!默认插槽的缩写写法不能和具名插槽混用!

5. 作用域插槽

2.6.0 版本以后,slot-scope 属性已经废弃,但依然可以使用

为了让插槽可以访问到子组件中的数据有时候非常有用。例如:

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <own-default v-slot:default="slotProps">
            {{ slotProps.user.firstname }}
        </own-default>
    </div>
</body>
<script>


    Vue.component('own-default', {
        template: '<div class="container">'+
                    '<slot :user="user"></slot>' +
                '</div>' ,
        data: () => {
            return {
                user: {
                    firstname: 'qian',
                    lastname: 'qian',
                }
            }
        }
    });

    new Vue({
        el: '#app'
    });
</script>

</html>

这里,实际上是提供了插槽的 props,这里的 <slot></slot> 标签绑定了组件中的 user 数据,所以在<slot></slot> 中,我们可以将 user.firstname 作为后备内容。在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字。

解构插槽的Prop

我们可以根据 ES2015 里面的规则,解构我们传入的插槽 prop,比如

<current-user v-slot="{ user: person }">
  {{ person.firstName }}
</current-user>

将 user 重新命名为 person。

6. 动态插槽名

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

7. 具名插槽的缩写

v-onv-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

<base-layout>
  <template #header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template #footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:

<!-- 这样会触发一个警告 -->
<current-user #="{ user }">
  {{ user.firstName }}
</current-user>

如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:

<current-user #default="{ user }">
  {{ user.firstName }}
</current-user>

8. 其他示例

**插槽 prop 允许我们将插槽转换为可复用的模板,这些模板可以基于输入的 prop 渲染出不同的内容。**在这种设计分装数据逻辑同时允许腹肌组件自定义部分布局的可以复用组件时是最有用的。

官网有这样的例子:

<html>

<head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
    <div id="app">
        <todo-list v-bind:todos="todos">
            <template v-slot:todo="{ todo: todo }">
                <span v-if="todo.isComplete">√</span>
                {{ todo.text }}
            </template>
        </todo-list>
    </div>
</body>
<script>
    Vue.component('todo-list', {
        props: ['todos'],
        template: '<ul><li v-for="todo in filteredTodos" v-bind:key="todo.id"><slot name="todo" v-bind:todo="todo">{{ todo.text }} </slot> </li></ul>',
        data: function() {
            return {
                filteredTodos: this.todos
            }
        }
    });

    new Vue({
        el: "#app",
        data: {
            todos: [{
                    key: 1,
                    text: 'Vue',
                    isComplete: true
                },
                {
                    key: 2,
                    text: 'is',
                    isComplete: false
                },
                {
                    key: 3,
                    text: 'interesting',
                    isComplete: true
                }
            ]
        }
    })
</script>

</html>

最后显示为:

在这里插入图片描述

其中

<template v-slot:todo="{ todo: todo }">

中的解构赋值可以写成 v-slot:todo="{ todo }" 这样的缩略写法。hart.js/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值