VUE3组件 (3) 关于 slot 插槽

前言

以下代码和内容的使用都是在setup中,未使用TS。

<script setup>
</script>

VUE3组件 (1) 关于defineProps()

VUE3组件 (2) 关于defineEmits()

VUE3组件 (4) 关于 Provide Inject 依赖注入


插槽使用

标题插口

在组件中除了将数据值当作props传入组件。有时需要将 模版传入到组件中,就需要使用 slot 插槽。
直接上代码
父组件中:

<section>
   <SlotItem><p>this is body</p></SlotItem> 
</section>

子组件

 <section>
    <p>this is item</p>
    <slot></slot>
 </section>

语法
在子组件中 使用 <slot></slot> 作为插槽的插口,父元素中的插槽内容将会渲染的位置。
上述代码最后会渲染成

 <section>
    <p>this is item</p>
    <p>this is body</p>
 </section>

插槽内容可以是任意合法的模板内容,包括文本、标签节点、自定义组件甚至是渲染函数的组件。
插槽内容可以是多层的

<section>
   <SlotItem>
	   <p>this is body</p>
	   <customElement /><!-- 自定义组件 -->
	   {{info}}<!-- 渲染到页面中 -->
   </SlotItem> 
</section>
<script setup>
  import { CustomDiv } from '@/component/hElement';
  import SlotItem from './slotItem.vue';
  import { ref } from 'vue';
  const info = ref('this is text');
</script>

渲染作用域

插槽内容无法访问子组件的数据,请牢记一条规则:

任何父组件模板中的东西都只被编译到父组件的作用域中。
而任何子组件模板中的东西都只被编译到子组件的作用域中。

解释
如果上述的 info 是定义在 SlotItem 中的变量,则在父组件中无法使用 {{info}} 的方式调用,即使是在插槽内容也不行。
只有在父组件中定义的变量才能在父组件使用。这点不会因为slot而改变。

默认内容

子组件

<section>    
    <slot>confirm</slot>
</section>

父组件中

<section>    
    <SlotItem></SlotItem>
</section>

如上 如果在子组件中有插槽接口,但是在父组件中并没有传递插槽内容,则在渲染时,会将 confirm 渲染到页面上。
如果在父组件的插槽内容有值。

<SlotItem>Cancel</SlotItem>

则在渲染时,会将 Cancel 渲染到页面上。

具名插槽

当一个组件需要多个插槽内容(如不同的渲染位置,不同的渲染样式)时,需要使用到 具名插槽。 语法:v-slot 指令,对应简写 #
代码:
子组件中

<section>
    <p>this is item</p>
    <slot></slot>
    <slot name="title"></slot>
    <slot name="body"></slot>
</section>

父组件中:

<section>
	<SlotItem>
      	<p>this is default</p>
      	<template #title>this is title</template>
      	<template #body>
        	<p>this is body</p>
      	</template>
      	<p>this is default2</p>
    </SlotItem>
</section>

在子组件中,还是使用slot 做为插口。只是在添加了 name 属性表示插槽名。并且在父组件中使用 template 来包裹插槽内容。关系为:
子中的<slot name="title"></slot> 插槽接口接收
父中 <template #title>this is title</template>
同理 body 也是如此渲染。
所以上述代码最后会渲染成。

this is item
this is default1
this is default2
this is title
this is body

发现上述代码和父组件中的书写顺序并不一样?this is default2 位置为什么会到 title 插槽之前呢?
因为 当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非 <template>节点都被隐式地视为默认插槽的内容
所以上述逻辑是,读取到 default1、default2 为默认插槽,放置到子组件<slot></slot> 的位置。再讲titlebody 插槽内容对应。这就是最后渲染的顺序与书写的顺序,不一致的原因。

动态插槽名

动态指令参数在 v-slot 上也是有效的

<section>
	<SlotItem>
      	<p>this is default</p>
      	<template #[area]>this is title</template>      
    </SlotItem>
</section>

area可以动态的修改为 title 或者 body

作用域插槽

渲染作用域 中,插槽内容无法访问到子组件的状态。
但是在实际中,会需要在父组件中使用子组件数据的情况。
列子:
子组件中定义两个变量

<script setup>
  import { ref } from 'vue';
  const txt = ref('A');
  const number = ref(0);
</script>

那如果要在父组件中使用txt和number的值要如何操作呢?
根据slot提供的语法,只要在 slot 插口,类似于props的形式绑定这两个attribute即可.
子组件代码:

<template>
  <section>
    <p>this is item</p>
    <slot name="body" :txt="txt" :number="number"></slot>
  </section>
</template>

<script setup>
  import { ref } from 'vue';
  const txt = ref('A');
  const number = ref(0);
</script>

父组件中

<SlotItem>
      <template #body="scope">{{ scope.txt }} {{ scope.number}}</template>
</SlotItem>

之前的插槽接口通过 #body 来表示。现在通过 #body="scope" 的形式接受到一个props对象。scope 只是个变量名称,可以是任意的字符串字面量。那如果是文章最开始默认插槽呢?也就是不具名插槽要怎么办?
默认插槽或者是未具名的插槽,实际有个默认值 default 上述代码如果用默认插槽则可写成
子组件

<slot :txt="txt" :number="number"></slot>

父组件

 <template #default="scope">{{ scope.txt }} {{ scope.number }}</template>

常见用法

  1. 在element-plus table组件中的自定义列功能就是作用域插槽的写法。
    一下代码为展示,详见: table 自定义列模板
<el-table-column label="Name" width="180">
    <template #default="scope">
      <el-popover effect="light" trigger="hover" placement="top" width="auto">
        <template #default>
          <div>name: {{ scope.row.name }}</div>
          <div>address: {{ scope.row.address }}</div>
        </template>
        <template #reference>
          <el-tag>{{ scope.row.name }}</el-tag>
        </template>
      </el-popover>
    </template>
</el-table-column>

上述代码中:
#default="scope" 就是默认插槽的 props对象。之后再在调用table组件的父页面中,调用了 scope中的对应数据。
这之中包裹的 <template #default><template #reference> 则是 element 的 popover组件的插槽写法。

  1. Vue Router 中的 <router-view></router-view> 配合 Vue中的 <component/>
  • App.vue中
<template>
   <router-view></router-view>
</template>
  • Layout.vue 组件中
<router-view v-slot="{ Component, route }">
    <component :is="Component" :key="route.path" />
</router-view>
  • 在router文件夹中的 index.js文件中 引入 Layout组件 并且在routes 数组中:
const Routes = [{
    path: '/component',
    component: Layout,
    redirect: '/component/page1',
    children: [{
      path: 'page1',
      component: () => import('@/views/page/page1.vue'),
      name: 'componentPage1',    
    }, {
      path: 'page2',
      component: () => import('@/views/page/page2.vue'),
      name: 'componentPage2',
    }]
  }]
  // 上述path ,组件路径为举例写法,具体根据实际情况修改。

Routes 中有多个页面时,都可以只引用 Layout 组件,具体的加载组件根据 componentimport 引入的为准。
Layout.vue <component /> 上的 is ,就会根据作用域插槽返回的 {Component} 属性,动态的渲染需要组件。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余九月丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值