什么是 slot ?
Vue 将 <slot>
元素作为承载分发内容的出口。插槽内可以包含任何模板代码,包括 HTML或其它组件。
在某些组件的模板中,有一部分区域需要父组件来指定
<!-- message组件:一个弹窗消息 -->
<div class="message-container"><div class="content"><!-- slot是vue的内置组件 —— 插槽、占位 --><slot></slot></div><button>确定</button><button>关闭</button>
</div><!-- 父组件App -->
<Message><div class="app-message"><p>App Message</p><a href="">detail</a></div>
</Message><!-- 最终的结果 -->
<div class="message-container"><div class="content"><div class="app-message"><p>App Message</p><a href="">detail</a></div></div><button>确定</button><button>关闭</button>
</div>
具名插槽
如果某个组件中需要父元素传递多个区域的内容,也就意味着需要提供多个插槽
为了避免冲突,就需要给不同的插槽赋予不同的名字
如果是默认插槽,可以不写
<template v-slot:default>
v-solt:header
可以简写为#header
<!-- Layout 组件 -->
<div class="layout-container"><header><!-- 我们希望把页头放这里,提供插槽,名为header --><slot name="header"></slot></header><main><!-- 我们希望把主要内容放这里,提供插槽,名为default --><slot></slot></main><footer><!-- 我们希望把页脚放这里,提供插槽,名为footer --><slot name="footer"></slot></footer>
</div><!-- 父组件App -->
<BaseLayout><template v-slot:header><h1>Here might be a page 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>
</BaseLayout>
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。
参见官网文档:插槽 — Vue.js
属性:
$slots
:用于访问父组件传递的普通插槽中的 vnode$scopedSlots
:用于访问父组件传递的所有用于生成 vnode 的函数(包括默认插槽在内)
可复用的列表组件
利用插槽可以制作可复用的组件。比如实现一个通用的列表组件,列表的行为是公共的、可复用的,列表中显示的内容使用组件时再指定。
实现思路:将列表数据(items)传递给列表组件,组件中使用 v-for 生成列表的框架,其中每个 item 通过 slot 展示,使用组件的代码中指定 slot 的内容。
列表组件
<li v-for="item in items" :key="item.id"><slot></slot>
</li>
这里 slot 中的内容是和 item 相关的,所以替换 slot 的内容时必须能够访问到 item 的数据。
<my-list :items="items"><div>{{item.label}}</div>
</my-list>
但是这样写是无效的,因为
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
此时,我们要实现父组件能够访问子组件中的数据(item),可以使用作用域插槽;
将 item 作为 <slot> 元素的一个特性绑定上去:绑定在 <slot> 元素上的特性被称为插槽 prop。
现在在父级作用域中,我们可以给 v-slot 带一个值来定义我们提供的插槽 prop 的名字。
列表组件
<li v-for="item in items" :key="item.id"><slot :item="item"></slot>
</li>
在父组件中访问子组件属性的方式:
<my-list :items="items" v-slot="slotProps"><div>{{slotProps.item.label}}</div>
</my-list>
这样写法太繁琐,可以使用 解构插槽 prop 简化:
<my-list :items="items" v-slot="{ item }"><div>{{item.label}}</div>
</my-list>
精读vue-virtual-scroller源代码,学习制作可复用组件(1) - 简书
深入理解插槽
例1:
注册了一个全局组件CompOne.vue
里面放着默认插槽,具名插槽,作用域插槽三种
在父组件中使用
可以把 <CompOne> 这个组件看做一个对象
父组件把整个对象传递给了子组件CompOne
例2:
使用render函数(以js文件的形式生成组件)CompTwo
div成功渲染
证明父组件中对应插槽是作为对象传到了子组件
子组件通过第二个属性 { slots } 解构获得了对象
默认插槽是作为返回值
作用域插槽是作为参数传递了
每一个传递的插槽本质上就是一个函数,这里传了3个函数
子组件获得插槽的内容,相当于调函数
调用 slots.default() 函数,得到的是虚拟节点,并且是一个数组,因为传递的节点可能有多个
要渲染到页面上,在返回值里面展开就可以得到了
动态插槽
名字是可变的