vue slot 插槽使用详解

目录

slot 介绍 

2.6.0以下版本

2.6.0及以上版本

作用域插槽说明

$slots 属性

slot 在组件开发中的使用


slot 介绍 

vue 对于前端的重要性不言而喻,而插槽对于 vue 的重要性亦是如此,在高阶组件的封装中离不开插槽的使用,所以学会使用 slot 对前端来讲很重要。

官网说:

Vue 实现了一套内容分发的 API,这套 API 的设计灵感源自 Web Components 规范草案,将 <slot> 元素作为承载分发内容的出口。

所以,插槽其实是 vue 提供的用于内容分发的 API,用 slot 来作为承载分发内容的出口而已。

如下组件,slot 元素将作为承载传入 content-box 组件所有内容的出口,即传入 content-box 组件的所有内容都将被渲染到 content-box 组件内部 slot 元素的位置。

slot 元素可以承载任何内容

<!-- content-box 组件内部 -->
<div
  class="content-box"
>
  <slot></slot>
</div>

使用: 

<content-box>
    当前所有内容都会被挂载到slot元素上
    <span>123</span>
    <el-input></el-input>
</content-box>

如果 <content-box> 组件内的 <template> 标签中没有包含一个 <slot> 元素,则在父组件中该组件起始标签和结束标签之间的任何内容都会被抛弃。 

即 <content-box> 标签中的内容都会被抛弃。

vue 提供的 slot 共有两个版本,在vue 2.6.0 及后代版本中 vue 为 slot 引入了新的语法,在日常开发中可能更多使用的是 2.6.0 新版本的语法,但是在一些UI组件的使用中仍需关注旧版本的语法,详细使用见官方文档:vue slot 插槽文档

下面说说不同版本 slot 的使用

2.6.0以下版本

声明

    // HelloWord 组件
    <!-- 默认插槽 -->
    <!-- 默认插槽的后备内容直接写在slot标签内就可以了 -->
    <slot>默认插槽的后备内容</slot>


    <!-- 具名插槽 -->
    <!-- <slot> 元素有一个特殊的属性:name。这个属性可以用来定义额外的插槽 -->
    <slot name="title">
      <!-- 后备内容 -->
      <span v-if="title">{
   
   {title}}</span>
    </slot>


    <!-- 作用域插槽  -->
    <slot name="title" :data="{firstName: 'firstName',lastName: 'lastName'}"></slot>

使用

    <!-- 父组件中 -->
    <HelloWorld>
      <!-- 默认插槽使用 -->
      <!-- 任何向组件传递的非具名插槽的内容都会被视为默认插槽的内容 -->
      <div>这是将被默认插槽接收的内容</div>


      <!-- 具名插槽使用 -->
      <!-- 在2.6.0之前版本 slot 属性可以直接使用在 template 或者 html 元素上 -->
      <!-- 通过slot属性可以向具名插槽传递内容 -->
      <div slot="title">这是将被具名插槽接收的内容</div>


      <!-- 作用域插槽使用 -->
      <!-- 在template 或者 html 元素上使用特殊的 slot-scope 属性可以接收传递给插槽的 prop -->
      <!-- 作用域插槽的作用:是为了更方便在父级作用域中访问子级作用拥有的数据 -->
      <!-- 即在父组件中使用子组件中的数据 -->
      <!--  -->
      <div slot="title" slot-scope="{data}">{
   
   {data.firstName}}</div>
    </HelloWorld>

2.6.0及以上版本

2.6.0及以下版本中 slot 插槽的使用方式与之前不同,声明方式相同:

声明

    // HelloWord 组件
    <!-- 默认插槽 -->
    <!-- 默认插槽的后备内容直接写在slot标签内就可以了 -->
    <!-- 子组件在父组件中使用,并不提供任何插槽内容时,后备内容生效 -->
    <slot>默认插槽的后备内容</slot>


    <!-- 具名插槽 -->
    <!-- <slot> 元素有一个特殊的属性:name。这个属性可以用来定义具名插槽 -->
    <slot name="title">
      <!-- 后备内容 -->
      <!-- 没有传递title插槽内容并且传递title属性值时生效 -->
      <span v-if="title">{
   
   {title}}</span>
    </slot>


    <!-- 作用域插槽  -->
    <!-- 绑定在 <slot> 元素上的属性被称为插槽 prop,不包含name -->
    <slot name="title" :data="{firstName: 'firstName',lastName: 'lastName'}"></slot>

使用

2.6以后版本, slot 使用方式变为 v-slot。

v-slot 只能添加在 <template> 上,只有一种例外情况

  <HelloWorld title="向HelloWord组件传递的title">
    <!-- 默认插槽 -->
    <!-- 任何向组件传递的非具名插槽的内容都会被视为默认插槽的内容 -->
    <div>这是将被默认插槽接收的内容</div>

    <!-- v-slot 只能添加在 template 元素上 -->
    <template v-slot>这是将被默认插槽接收的内容</template>
    或者
    <!-- 默认插槽带有隐含的名字 default -->
    <template v-slot:default>这是将被默认插槽接收的内容</template>


    <!-- 具名插槽 -->
    <!-- 在一个 <template> 元素上使用 v-slot 指令,
         并以 v-slot 的参数的形式提供插槽名称 -->
    <template v-slot:title>这是将被具名插槽接收的内容</template>


    <!-- 作用域插槽 -->
    <!-- 使用带值的 v-slot 来接收我们提供的插槽 prop 的值 -->
    <!-- 作用域插槽的作用:是为了更方便在父级作用域中访问子级作用拥有的数据 -->
    <!-- 即在父组件中使用子组件中的数据 -->
    <!--  -->
    <template v-slot="{data}">{
   
   {data.firstName}}</template>
  </HelloWorld>

具名插槽的缩写

跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #

例如 v-slot:header 可以被重写为 #header。

作用域插槽说明

先看vue 官方对于组件模板作用域及编译的说明:

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

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

 所以在父集作用域中无法访问子作用域中的数据。

这就是为什么需要使用作用域插槽:因为在父组件作用域中不能访问子组件作用域中的数据。

有时让插槽内容能够访问子组件中才有的数据是很有用的,如下在父组件中访问子组件数据,会报错。

子组件模板:

<template>
  <div class="hello">
    <slot name="title"></slot>
  </div>
</template>

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

父组件模板:

  <HelloWorld>
    <template v-slot:title>
      <div>这是传递给title solt的内容</div>
      <div>{
   
   {data.firstName}}</div>
    </template>
  </HelloWorld>

报错如下:

 所以为了解决这个问题,就需要使用作用域插槽,在子组件中可以将 data 绑定为 slot 的属性,在父组件作用域中使用带值得 v-slot 来接收,如下:

// 子组件模板
<template>
  <div class="hello">
    <slot name="title" :data="data"></slot>
  </div>
</template>
  // 父组件模板
  <HelloWorld>
    <template v-slot:title="{data}">
      <div>这是传递给title solt的内容</div>
      <div>{
   
   {data.firstName}}</div>
    </template>
  </HelloWorld>

这样在父组件模板内便可以访问到子组件中的数据了。

注意:在父组件内,子组件模板内的作用域依然属于父集,即 <HelloWord> 模板内的作用域依然属于父集作用域。

$slots 属性

$slots 用来访问被插槽分发的内容,在组件开发中承载了非常重要的作用,详情请看官网 vue $slots

2.6之后与之前版本的 $slots 属性略有不同,下面做一个简单对比

子组件模板如下:

<template>
  <div class="hello">
    <slot></slot>
    <slot name="title"></slot>
  </div>
</template>

<script>
export default {
    mounted () {
      console.log(this.$slots, 'slot===2.x')
    },
}
</script>

 2.6版本以下组件使用:

<HelloWorld>
    <div>这是将被默认插槽接收的内容</div>
    <div slot="title">这是传递给title插槽的内容</div>
</HelloWorld>

输出如下:

2.6 及以上版本使用:

<HelloWorld >
    <template v-slot>这是将被默认插槽接收的内容</template>
    <template v-slot:title>
      <div>这是传递给title solt的内容</div>
    </template>
</HelloWorld>

 输出如下:

 很明显两个版本 $slots 属性值得类型是不同的,更深层次这里不做讨论,推荐查看:

Vue3.0破坏性变化----$slots

slot 在组件开发中的使用

在组件封装中合理的使用 slot 可以开发更加高效强大的组件,重点是对于 $slots 属性的应用。

下面贴上 u-cell 组件的源码,学习 slot 的封装技巧。

<view 
    class="u-cell__body" 
	:class="[ center && 'u-cell--center', size === 'large' && 'u-cell__body--large']"
	:style="{paddingLeft: paddingLeft+'px',paddingRight: paddingRight+'px',paddingTop: paddingTop+'px',paddingBottom: paddingBottom+'px'}"
>
		<view class="u-cell__body--top">
			<view class="u-cell__body__content">
				<view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon">
					<slot name="icon" v-if="$slots.icon">
					</slot>
					<u-icon v-else :name="icon" :custom-style="iconStyle" :size="size === 'large' ? 22 : 18"></u-icon>
				</view>
				<view class="u-cell__title">
					<slot name="title">
						<text v-if="title" class="u-cell__title-text" :style="[titleTextStyle]"
							:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__title-text--large']">{
   
   { title }}</text>
					</slot>
					<slot name="label">
						<text class="u-cell__label" v-if="label"
							:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__label--large']">{
   
   { label }}</text>
					</slot>
				</view>
			</view>
			<slot name="value">
				<text 
					class="u-cell__value"
					:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__value--large']"
					v-if="!$u.test.empty(value)"
					:style="[valueTextStyle]"
				>
					{
   
   { value }}
				</text>
			</slot>
			<view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink"
				:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
				<slot name="right-icon" v-if="$slots['right-icon']">
				</slot>
				<u-icon v-else :name="rightIcon" :custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'"
						:size="size === 'large' ? 18 : 16"></u-icon>
			</view>
		</view>
		<view class="u-cell__body--bottom" v-if="$slots.bottom">
			<slot name="bottom"></slot>
		</view>
	</view>

猜你喜欢

转载自blog.csdn.net/lwx931449660/article/details/122833901
今日推荐