以下代码和内容的使用都是在setup中,未使用TS。
<script setup>
script>
VUE3组件 (1) 关于defineProps()
VUE3组件 (2) 关于defineEmits()
VUE3组件 (4) 关于 Provide Inject 依赖注入
在组件中除了将数据值当作props传入组件。有时需要将 模版传入到组件中,就需要使用 slot 插槽。
直接上代码
父组件中:
<section>
<SlotItem><p>this is bodyp>SlotItem>
section>
子组件
<section>
<p>this is itemp>
<slot>slot>
section>
语法
在子组件中 使用
作为插槽的插口,父元素中的插槽内容将会渲染的位置。
上述代码最后会渲染成
<section>
<p>this is itemp>
<p>this is bodyp>
section>
插槽内容可以是任意合法的模板内容,包括文本、标签节点、自定义组件甚至是渲染函数的组件。
插槽内容可以是多层的
<section>
<SlotItem>
<p>this is bodyp>
<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>confirmslot>
section>
父组件中
<section>
<SlotItem>SlotItem>
section>
如上 如果在子组件中有插槽接口,但是在父组件中并没有传递插槽内容,则在渲染时,会将 confirm 渲染到页面上。
如果在父组件的插槽内容有值。
<SlotItem>CancelSlotItem>
则在渲染时,会将 Cancel 渲染到页面上。
当一个组件需要多个插槽内容(如不同的渲染位置,不同的渲染样式)时,需要使用到 具名插槽。 语法:v-slot
指令,对应简写 #
。
代码:
子组件中
<section>
<p>this is itemp>
<slot>slot>
<slot name="title">slot>
<slot name="body">slot>
section>
父组件中:
<section>
<SlotItem>
<p>this is defaultp>
<template #title>this is titletemplate>
<template #body>
<p>this is bodyp>
template>
<p>this is default2p>
SlotItem>
section>
在子组件中,还是使用slot
做为插口。只是在添加了 name 属性表示插槽名。并且在父组件中使用 template
来包裹插槽内容。关系为:
子中的
插槽接口接收
父中 this is title
。
同理 body 也是如此渲染。
所以上述代码最后会渲染成。
this is item
this is default1
this is default2
this is title
this is body
发现上述代码和父组件中的书写顺序并不一样?this is default2 位置为什么会到 title 插槽之前呢?
因为 当一个组件同时接收默认插槽和具名插槽时,所有位于顶级的非 节点都被隐式地视为默认插槽的内容 。
所以上述逻辑是,读取到 default1、default2 为默认插槽,放置到子组件
的位置。再讲title、body 插槽内容对应。这就是最后渲染的顺序与书写的顺序,不一致的原因。
动态指令参数在 v-slot 上也是有效的
<section>
<SlotItem>
<p>this is defaultp>
<template #[area]>this is titletemplate>
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 itemp>
<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>
常见用法
<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中的对应数据。
这之中包裹的 和
则是 element 的 popover组件的插槽写法。
配合 Vue中的
<template>
<router-view>router-view>
template>
<router-view v-slot="{ Component, route }">
<component :is="Component" :key="route.path" />
router-view>
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 中
上的 is ,就会根据作用域插槽返回的 {Component} 属性,动态的渲染需要组件。