Jamey's Jamey's
首页
导航站
  • 学习专栏

    • 《HTML》笔记
    • 《CSS》笔记
    • 《JavaScript》笔记
    • 《Vue》笔记
    • 《Git》笔记
    • 《规范》笔记
    • 《软技能》笔记
    • 《面试》笔记
    • 《持续集成&交付&部署》笔记
  • 踩坑专栏

    • 《Element-UI 实践系列》笔记
    • 《移动端 实践系列》笔记
    • 《综合》笔记
  • 配置专栏

    • 《环境系列》笔记
  • 极空间

    • Docker
  • 影视

    • movie
  • 编辑器笔记

    • 开发编辑器
  • 浏览器笔记

    • Chrome
  • Mac笔记

    • Mac
  • 跨界学习

    • 运营
  • 破解合集

    • 破解
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 书单
    • 关于

Jamey

首页
导航站
  • 学习专栏

    • 《HTML》笔记
    • 《CSS》笔记
    • 《JavaScript》笔记
    • 《Vue》笔记
    • 《Git》笔记
    • 《规范》笔记
    • 《软技能》笔记
    • 《面试》笔记
    • 《持续集成&交付&部署》笔记
  • 踩坑专栏

    • 《Element-UI 实践系列》笔记
    • 《移动端 实践系列》笔记
    • 《综合》笔记
  • 配置专栏

    • 《环境系列》笔记
  • 极空间

    • Docker
  • 影视

    • movie
  • 编辑器笔记

    • 开发编辑器
  • 浏览器笔记

    • Chrome
  • Mac笔记

    • Mac
  • 跨界学习

    • 运营
  • 破解合集

    • 破解
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 书单
    • 关于
  • 基础

  • 组件

    • 一次性讲明白 Vue 插槽 slot
      • 一. 前言
      • 二. 插槽是什么
      • 三. 插槽的作用
      • 四. 插槽的分类
        • 1. 默认插槽
        • 2. 具名插槽
        • 3. 作用域插槽
    • 计算属性怎么传参?
  • 路由

  • 自定义指令

  • 实践踩坑

  • 知识点

  • 《Vue》笔记
  • 组件
Jamey
2022-03-02
目录

一次性讲明白 Vue 插槽 slot

# 一次性讲明白 Vue 插槽 slot

# 一. 前言

Vue 官方文档中在“组件基础”内容中提到,组件可以通过插槽分发内容,那么插槽是怎么使用的呢?它要解决什么场景的问题呢?

我们在构建页面过程中,一般会把比较多的公共的部分抽取出来作为一个单独的组件,但是在实际使用这个组件的时候,却又不能完全的满足需求,我希望在这个组件中添加一点东西,这时候我们就需要用到插槽来分发内容。

注意:以下的内容是基于 vue 版本 2.6.0 起

# 二. 插槽是什么

下面看一个例子:

  • 写一个父组件: test.vue
<template>
  <div>
    <div>大家好我是父组件</div>
    <myslot>
      <p>测试一下吧内容写在这里了能否显示</p>
    </myslot>
  </div>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  • 写一个子组件:myslot.vue
<template>
  <div>
    <div>我是子组件</div>
  </div>
</template>

<script></script>

<style></style>
1
2
3
4
5
6
7
8
9

运行代码,发现,最终渲染的效果是:

大家好我是父组件
我是子组件
1
2

那如果我想实现显示父组件中 p 标签的内容怎么办?

修改子组件:myslot.vue

<template>
  <div>
    <div>我是子组件</div>
    <p>现在测试一下slot</p>
    <slot></slot>
  </div>
</template>

<script></script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11

运行代码,可以看到以下效果:

大家好我是父组件
我是子组件
现在测试一下slot
测试一下吧内容写在这里了能否显示
1
2
3
4

官方文档对于插槽的应用场景是这样描述的:

我们经常需要向一个组件传递内容,Vue 自定义的 <slot> 元素让这变得非常简单。只要在需要的地方加入插槽就行了 ———— 就这么简单!

结合上面的例子来理解就是这样的:

  1. 父组件在引用子组件时希望向子组件传递模板内容 <p>测试一下吧,内容写在这里了能否显示</p>。
  2. 字符键让父组件传过来的模板内容在所在的位置显示。
  3. 子组件中的 <slot> 就是一个槽,可以接受父组件传过来的模板内容,<slot> 元素自身将被替换。
  4. <myslot></myslot> 组件没有包含一个 <slot> 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。

# 三. 插槽的作用

让用户可以拓展组件,去更好的复用组件和对其做定制化处理。

# 四. 插槽的分类

# 1. 默认插槽

在一个 <submit-button> 组件中:

<button type="submit">
  <slot></slot>
</button>
1
2
3

我们可能希望这个 <button> 内绝大多数情况下都渲染文本“Submit”,但是有时候却希望渲染文本为别的东西,那怎么实现呢?

我们可以将“Submit”作为后备内容,我们可以将它放在 <slot> 标签内:

<button type="submit">
  <slot>Submit</slot>
</button>
1
2
3

现在当我在一个父组件中使用 <submit-button> 并且不提供任何插槽内容时:

<submit-button></submit-button>
1

后备内容“Submit”将会被渲染:

<button type="submit">
  Submit
</button>
1
2
3

但是如果我们提供内容:

<submit-button>
  Save
</submit-button>
1
2
3

则这个提供的内容将会被渲染从而取代后备内容:

<button type="submit">
  Save
</button>
1
2
3

# 2. 具名插槽

有时我们写了一个子组件,我们希望:

<template>
  <div class="container">
    <header>
      <!-- 我们希望把页头放这里 -->
    </header>
    
    <main>
      <!-- 我们希望把主要内容放这里 -->
    </main>
    
    <footer>
      <!-- 我们希望把页脚放这里 -->
    </footer>
  </div>
</template>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

对于这样的情况,<slot> 元素有一个特殊的 attribute:name。这个 attribute 可以用来定义额外的插槽:

<template>
  <div class="container">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13

一个不带 name 的 <slot> 出口会带有隐含的名字 “default”。

父组件在向具名插槽提供内容的时候,我们可以在一个 <template> 元素上使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称:

<template>
  <myslot>
    <div>大家好我是父组件</div>
    <template v-slot:header>
      <h1>Here might be a page title</h1>
    </template>

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

    <template v-slot:footer>
      <p>Here's footer info</p>
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

最终的渲染结果可以看到:

Here might be a page title
大家好我是父组件
A paragraph for the main content.

And another one.

Here's footer info
1
2
3
4
5
6
7

父组件中会向子组件中具名传递对应的模板内容,而没有指定名字的模板内容会传递给子组件中不带 name 的 <slot>。

当然,如果父组件中

<template v-slot:default>
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>
</template>
1
2
3
4

同样是传递给子组件中不带 name 的 <slot>。

注意:

  • v-slot 只能添加在 <template> 上。
  • 具名插槽在书写的时候可以使用缩写,v-slot 用 # 来代替
<template>
  <myslot>
    <div>大家好我是父组件</div>
    <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 footer info</p>
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 3. 作用域插槽

这里主要解决的是 父组件在向子组件插槽传递模板内容时存在访问子组件数据的问题

还记得默认插槽吗?如果子组件中写在 <slot> 标签内后备内容是与 该组件的 data 属性双向数据绑定的

<template>
  <div>
    <span>
      <slot>{{user.firstName}}</slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName:'gerace',
          lastName:'haLi'
        }
      }
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

父组件在引用子组件时,希望能够换掉备用内容

<template>
  <myslot>{{ user.firstName }}</myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

运行代码这时你会发现提示报错

Property or method "user" is not defined on the instance but referenced during render.
TypeError: Cannot read property 'firstName' of undefined
1
2

这里为什么?vue 官方文档给出了答案:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

那应该怎么解决这个问题呢?

为了让 user 在父级的插槽内容中可用,我们可以将 user 作为 <slot> 元素的一个 attribute 绑定上去:

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>
1
2
3
4
5

绑定在 <slot> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:

<template>
  <myslot>
    <template v-slot:default="slotProps">
      {{ slotProps.user.firstName }}
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

上面例子,我们选择将包含所有插槽 prop 的对象命名为 slotProps,但你也可以使用任意你喜欢的名字。

针对上面只给默认插槽传递模板内容的例子,在写法上可以采用默认插槽的缩写语法

<template>
  <myslot v-slot:default="slotProps">
     {{ slotProps.user.firstName }}
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上面的代码也可以直接改为:

<template>
  <myslot v-slot="slotProps">
     {{ slotProps.user.firstName }}
  </myslot>
</template>
1
2
3
4
5

注意: 默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确

<template>
  <myslot v-slot="slotProps">
     {{ slotProps.user.firstName }}
     <template v-slot:other="otherSlotProps">
   		slotProps is NOT available here
     </template>
  </myslot>
</template>
1
2
3
4
5
6
7
8

下面再多看一下多个插槽的情况:

  • 子组件:
<template>
  <div>
    <span>
      <slot v-bind:userData="user" name="header">
        {{ user.msg }}
      </slot>
      <slot v-bind:hobbyData="hobby" name="footer">
        {{ hobby.fruit }}
      </slot>
    </span>
  </div>
</template>

<script>
  export default {
    data () {
      return {
        user:{
          firstName: 'gerace',
          lastName: 'haLi',
        },
        hobby:{
          fruit: "apple",
          color: "blue"
        }
      }
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  • 父组件:
<template>
  <myslot>
    <template v-slot:header="slotProps">
      {{ slotProps.userData.firstName }}
    </template>
    <template v-slot:footer="slotProps">
      {{ slotProps.hobbyData.fruit }}
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

针对多个插槽的情况,在写法上可以解构插槽 prop,父组件的写法如下:

<template>
  <myslot>
    <template v-slot:header="{userData}">
      {{ userData.firstName }}
    </template>
    <template v-slot:footer="{hobbyData}">
      {{ hobbyData.fruit }}
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

在具名插槽的介绍部分有讲过,具名插槽可以使用缩写,v-slot 可以使用 # 来代替,所以以上代码可以写成:

<template>
  <myslot>
    <template #header="{userData}">
      {{ userData.firstName }}
    </template>
    <template #footer="{hobbyData}">
      {{ hobbyData.fruit }}
    </template>
  </myslot>
</template>

<script>
  import myslot from './myslot';
  export default {
    components: {
      myslot
    }
  }
</script>

<style></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

但是需要注意的是,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:

<!-- 这样会触发警告 -->
<template>
  <myslot>
    <template #="{userData}">
      {{ userData.firstName }}
    </template>
    <template #="{hobbyData}">
      {{ hobbyData.fruit }}
    </template>
  </myslot>
</template>
1
2
3
4
5
6
7
8
9
10
11
#Vue.js2
上次更新: 2022/07/08, 18:18:02
Class 与 Style 动态绑定
计算属性怎么传参?

← Class 与 Style 动态绑定 计算属性怎么传参?→

Theme by Vdoing | Copyright © 2017-2023 Jamey | blog 闽ICP备19022664号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式