Vue 中的插槽内容穿透和配置参数过滤
作者:互联网
需求描述
(以element ui组件为基础)设计一种嵌套结构,从上到下依次是:
- 卡片组(
dyn-card-group
) - 卡片(
dyn-card
) - 布局组(
dyn-layout-group
,包含el-row
和el-col
)
以下实现是通过配置参数的方式动态渲染组件,避免重复书写视图模板。配置参数严格按照组件结构声明。
将“布局组”组件作为插槽内容传入“卡片组”组件,并穿透到“卡片”组件内,“布局组”会获取并绑定“卡片”组件过滤后的布局数组,以确保实际生成的页面严格遵照配置参数的结构。
由于“卡片组”和“卡片”组件是独立的,当只有一个卡片时可以不渲染“卡片组”直接生成“卡片”,更加灵活。
思路分析
Vue 的插槽语法允许将任意符合要求的文本、表达式或组件传入到子组件中。因此,可以通过嵌套插槽的方式,将传入子组件插槽的内容穿透到更深层次的子组件中,并且可以获取到深层次子组件的插槽所绑定的数据。
定义需要穿透的插槽:
<!-- 父组件 -->
···
<!-- 将父组件的插槽内容传入子组件的插槽 -->
<template v-slot:childSlotName="{childData}">
<!-- 定义父组件的插槽,接收外部传入的内容 -->
<slot name="parentSlotName" :parentData="childData"></slot>
</template>
···
<!-- 子组件 -->
···
<slot name="childSlotName" :childData="dataInChild"></slot>
···
代码示例
配置参数 cardList
表示一个卡片组,组内包含两个卡片,每个卡片各自包含两个 el-row
以及若干 el-col
。
卡片组(dyn-card-group.vue):
<template>
<div class="dyn-card-group">
<dyn-card
:card="card"
v-for="(card, index) in cardList"
:key="index"
>
<!-- 插槽内容穿透到子组件,并获取子组件过滤后绑定的参数 -->
<template v-slot:children="{children}">
<slot
name="children"
:children="children"
>
</slot>
</template>
</dyn-card>
</el-card>
</div>
</template>
<script>
import dynCard from "./dyn-card.vue";
export default {
name: "dyn-card-group",
props: {
cardList: Array,
},
components: {
dynCard,
},
};
</script>
卡片(dyn-card.vue):
<template>
<div class="dyn-card">
<el-card>
<div slot="header">
<span>{{ card.cardTitle }}</span>
</div>
<!-- 父组件插槽的内容会穿透到这里,并获取此处绑定的参数 -->
<slot
name="children"
:children="card.children"
></slot>
</el-form-item>
</el-card>
</div>
</template>
<script>
export default {
name: "dyn-card",
props: {
card: {
cardTitle: String,
children: Array,
},
},
};
</script>
布局组(dyn-layout-group.vue):
<template>
<div class="dyn-layout-row-group">
<el-row
:type="row.type"
:gutter="row.gutter"
:class="row.class"
:justify="row.justify"
:align="row.align"
v-for="(row, index) in rowList"
:key="index"
>
<el-col
:span="col.span"
:offset="col.offset"
:push="col.push"
:pull="col.pull"
v-for="(col, index) in row.colList"
:key="index"
>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
name: "dyn-layout-row-group",
data() {
return {};
},
props: {
rowList: {
requred: true,
},
},
};
</script>
统一导出各组件 index.js:
import dynCardGroup from './dyn-card-group.vue';
import dynCard from './dyn-card.vue';
import dynLayoutGroup from './dyn-layout-group.vue';
export { dynCardGroup, dynCard, dynLayoutGroup};
在主模块中组合三个组件并传入参数:
<template>
<div class="container">
<dyn-card-group :cardList="cardList">
<!-- 布局 -->
<template v-slot:children="{children}">
<dyn-layout-group :rowList="children">
<!-- 字段 -->
<template v-slot:children>
<div class="grid-content bg-purple">栅格</div>
</template>
</dyn-layout-group>
</template>
</dyn-card-group>
</div>
</template>
<style lang="scss">
.el-row {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.bg-purple {
background: rgb(218, 149, 218);
}
</style>
<script>
import {
dynCardGroup,
dynLayoutGroup,
} from "@/components/index";
export default {
name: "container",
data() {
return {
cardList: [
{
cardTitle: "卡片标题",
// 这里的 children 属性是根据“卡片”组件的 children 属性命名的,
// 嵌套层级增加时会导致层级辨识困难,
// 设计时考虑到实际嵌套的组件是动态的,开发时无法确定子组件的名称,因此使用了适用范围更大的抽象命名 children,
// 优化方法可以考虑将对象拆分,将此处的布局数组在外部定义,然后引用到此处
children: [
{
gutter: 10,
colList: [{ span: 12 }, { span: 12 }],
},
{
gutter: 10,
colList: [{ span: 6 }, { span: 6 }, { span: 6 }, { span: 6 }],
},
],
},
{
cardTitle: "卡片标题",
children: [
{
gutter: 20,
colList: [{ span: 12 }, { span: 12 }],
},
{
type: "flex",
justify: "space-around",
gutter: 30,
colList: [{ span: 6 }, { span: 6 }, { span: 6 }],
},
],
},
],
};
},
components: {
dynCardGroup,
dynLayoutGroup,
},
};
</script>
输出效果:
总结
插槽穿透:
- 通过以上方式可以实现插槽内容的穿透,不过这种方式只适合被穿透的组件与穿透到的目标组件具有强耦合关系的场景。这种情况下通过可以将两个组件作为统一的整体同时开发与维护,比如示例中的“卡片组”与“卡片”。嵌套层级最好不超过3层,否则会比较难维护。
过滤配置参数:
- 通过以上方式过滤配置参数,实际上只绑定了组件的相对层级,也就是组件的祖孙关系(甚至可以实现反向祖孙关系),各组件之间仍然是独立的。修改某个组件的结构和内部数据不会影响其他组件,比如修改布局组件不会影响卡片组件,反之亦然。
- 在使用时,可以将任意的其他组件嵌入到布局和卡片之间,只要保证配置参数是严格符合组件的视图结构即可。配置参数被传入到各级组件时会根据结构分层以及分组,不同层级和不同分组之间不会相关影响,保证了组件之间的低耦合度。
标签:Vue,span,卡片,插槽,穿透,dyn,组件,card 来源: https://www.cnblogs.com/cjc917/p/16214033.html