14-Vue插槽(slot),制作可复用组件

什么是 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() 函数,得到的是虚拟节点,并且是一个数组,因为传递的节点可能有多个

 

 要渲染到页面上,在返回值里面展开就可以得到了

 

 动态插槽

 名字是可变的

猜你喜欢

转载自blog.csdn.net/iaz999/article/details/131650398