(以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>
输出效果:
插槽穿透:
过滤配置参数: