VUE3组件 (3) 关于 slot 插槽
VUE3组件slot 插槽
前言
以下代码和内容的使用都是在setup中,未使用TS。
<script setup>
</script>
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>
的位置。再讲title、body 插槽内容对应。这就是最后渲染的顺序与书写的顺序,不一致的原因。
动态插槽名
动态指令参数在 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>
常见用法
- 在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组件的插槽写法。
- 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
组件,具体的加载组件根据 component 中 import 引入的为准。
Layout.vue 中 <component />
上的 is ,就会根据作用域插槽返回的 {Component} 属性,动态的渲染需要组件。
更多推荐
所有评论(0)