@createTime: 2023/04/17 @updateTime: 2023/04/26
前言
之前与外企的伙计交流感情后得知他们的前端开发思路,于是乎便深入了解了这一套开发,深受震撼,写一篇文章记录记录 ( ̄︶ ̄)↗
概念
不听不听王八念经
- atoms 原子层
- 只有数据展示和样式,最好不能涉及代码逻辑
- molecules 分子层
- 导入并组合atoms(原子),会涉及部分样式调整(比如说间距,位置),会涉及代码逻辑(比如数据绑定,原子间的相互交互逻辑,应用层给的数据处理)
- organism 应用层(注:直译是“有机体”)
- 导入bing组合molecules(分子),建议只涉及接口请求(只是建议,毕竟分子层优先满足公用,不能满足一些应用层特殊情况)
- hooks 逻辑层(vue2能把函数封装成公共函数就用吧)
- 将常用的逻辑封装成公共函数,写入这里面,需要调用就导入调用
示例
之前写的一团糟,故写了个demo方便理解(vue2的写法)
table的二次封装
第零步,组件写在哪儿
别笑,我是认真的
(~ ̄(OO) ̄)ブ
分析:组件涉及展示和操作,还涉及很多其他组件相互配合(如进度条,标签),所以说放在molecules分子层
第一步,确定数据来源
<!-- demo部分代码
./src/components/molecules/MyTable.vue:2 -->
<a-table
class="my-table"
row-key="id"
:loading="loading"
:bordered="true"
:columns="tableColumns"
:data-source="dataSource"
:pagination="pagination"
>
......
这里可以看见部分要用的table组件的参数,这里我们要做第一步分析,分析它的数据是 从父组件获取 还是 直接配置,这里我就直接分析了
- 父组件获取:列配置
columns、列数据dataSource、加载状态loading - 直接配置:key取值
rowKey、边框bordered、分页设置pagination
当然,还有其他可配置项,如 滚动scroll、表标题title、部分自定义功能如选择框,反选rowSelection等
第二步,简化数据输入
确定完父元素所有传入值后,对每一个值进行分析
- 列配置
columns:大部分配置的值和key值绑定,故可以简化 - 列数据
dataSource:数据来源于后端JSON/JSONP,无法判断内容,故不可简化 - 加载状态
loading:大佬,这个能简化么(╬▔皿▔)╯
对columns进行简化:
// ./src/components/organisms/PageTable.vue:20
const columns: myTableColumns = [
{ key: 'name', value: '名称', type: 'string' },
{ key: 'status', value: '状态', type: 'badge' },
{ key: 'dateTime', value: '时间', type: 'dateTime' },
{ key: 'progress', value: '进度条', type: 'progress' },
{ key: 'others', value: '操作', type: 'others' }
]
myTableColumns类型
// ./src/components/types/myTable.ts:3
export type myTableColumn = {
// 关键词, 如'name'之类的
key: string
// 展示文本, 如'名称'之类
value: string
// 展示类型,这里可以用枚举限制, 如展示'进度条'、'标签'、'徽标'
type: string
// 根据业务需要,请自己添加其他东西
// width?: number
}
export type myTableColumns = myTableColumn[]
第三步,补全简化数据
将上述的columns代码补全成ant-design-vue能识别的类型
// ./src/components/molecules/MyTable.vue:53
computed: {
tableColumns (vm: { columns: columns }) { // vm = this
return vm.columns.map(setColumn)
}
}
因为列配置的细节有些许多,故我将列配置的细节单独导出成hooks
import { myTableColumn, antTableColumn } from '../types/myTable'
import { h } from 'vue'
import MyProgress from '../atoms/MyProgress.vue'
import MyBadge from '../atoms/MyBadge.vue'
import MyTime from '../atoms/MyTime.vue'
/** 将外层配置的列选项转化成 ant-table 能识别的column
*
* @param item: {myTableColumn}
*/
export const setColumn = (item: myTableColumn): antTableColumn => {
const out: antTableColumn = {
title: item.value,
dataIndex: item.key,
key: item.key,
ellipsis: true
}
if (item.type === 'others') {
out.scopedSlots = { customRender: item.key }
} else {
out.customRender = (text) => {
// 对 type进行处理,这里做示例
switch (item.type) {
case 'progress':
return h(MyProgress, {
props: { data: text, color: '#18e87f' },
domProps: {}
})
case 'badge':
let color = '' // eslint-disable-line no-case-declarations
let str = '' // eslint-disable-line no-case-declarations
switch (text.toString()) {
case '1':
color = '#2db7f5'
str = '正常'
break
case '2':
color = '#fadb14'
str = '下线'
break
case '3':
color = '#f5222d'
str = '错误'
break
default:
color = '#414141'
str = '未知'
}
return h(MyBadge, {
props: { data: str, color: color },
domProps: {}
})
case 'dateTime':
return h(MyTime, { props: { time: text } })
default:
return text || '--'
}
}
}
return out
}
export default {}
- 这里说一下,列表展示中会有按钮操作,如编辑、启/禁用等,所以说一定要设置
others处理 - 里面涉及了一些其他的没见过的以
My开头的组件,下一个涉及的部分
第四步,封装特殊组件
我这里是封装了 进度条,徽标数,时间显示(这个可以用hooks代替的)
<template>
<a-badge
class="my-badge"
:color="color || '#2db7f5'"
:text="data"
/>
</template>
<script>
import { Badge as ABadge } from 'ant-design-vue'
export default {
name: 'MyBadge',
components: { ABadge },
props: ['data', 'color']
}
</script>
<style scoped>
</style>
- props直接写数组就行,降低代码量
- 若是不得不涉及变量计算的,统一使用computed处理
- 封装后统一写在
atoms内部
description的二次封装
第零步
别想了,写在 molecules 里面
第一步,数据来龙去脉
<!-- demo部分代码
./src/components/molecules/MyDescription.vue:2 -->
<a-descriptions class="my-description" size="default" bordered :column="column" :title="title">
这里就直接上分析了
- 父组件获取:标题
title、列数量columns - 直接配置:尺寸
size、边框border - 其他可配项:布局
layout
聪明的你一定发现了,这个数据怎么展示呢?
description有它的儿子description-item,咱对它儿子进行循环就行了(总感觉怪怪的),循环数组一定得从父组件内获取,再根据父组件传入的展示对象展示即可
<!-- demo部分代码
./src/components/molecules/MyDescription.vue:4 -->
<template v-for="(item, index) of config">
<a-descriptions-item
:key="index"
:label="item.label"
:span="item.span"
>
<my-level v-if="item.value === 'level'" :level="value[item.value]" />
<my-time v-else-if="item.method === 'dateTime'" :time="value[item.value]" format="YYYY-MM-DD hh:mm:ss" />
<div v-else v-html="value[item.value]" />
</a-descriptions-item>
</template>
props: {
value: {
type: Object,
default: () => ({})
},
config: {
type: Array,
default: () => ([])
},
title: {
type: String,
default: () => '基本信息'
},
column: {
type: Number,
default: () => 4
}
}
第二步,规范传入类型
直接上父组件吧,方便理解
<template>
<div class="description-show">
<my-description
:config="descriptionConfig"
:value="descriptionInfo"
/>
</div>
</template>
<script lang="ts">
import MyDescription from '../molecules/MyDescriptions.vue'
export default {
name: 'DescriptionShow',
components: { MyDescription },
data () {
return {
descriptionConfig: [
{ label: '名称', value: 'name', span: 1 },
{ label: '类型', value: 'type', span: 1 },
{ label: '级别', value: 'level', span: 1 },
{ label: '创建时间', value: 'createTime', span: 1, method: 'dateTime' },
{ label: '次数', value: 'count', span: 1 },
{ label: '更新时间', value: 'updateTime', span: 1, method: 'dateTime' },
{ label: '描述', value: 'description', span: 2 }
],
descriptionInfo: {
name: '张三',
type: '部长',
level: '7',
createTime: 1662476859719,
count: 114514,
updateTime: 1682476859719,
description: '这是一个很牛的大佬,我说的是真的'
}
}
}
}
</script>
<style scoped>
</style>
这里我偷懒了,应该设置config输入的数据类型
= ̄ω ̄=
第三步,分析调用原子
直接上源码
<!-- ./src/components/molecules/MyDescription.vue:9 -->
<my-level v-if="item.value === 'level'" :level="value[item.value]" />
<my-time v-else-if="item.method === 'dateTime'" :time="value[item.value]" format="YYYY-MM-DD hh:mm:ss" />
<div v-else v-html="value[item.value]" />
按照原型图、UI设计图调用封装的atoms组件,完成数据展示
后话
我写的demo层级结构是这样的
components
atoms [MyBadge.vue, MyLevel, MyProgress, MyTime]
hooks [myTable]
molecules [MyDescription, MyTable]
organisms [DescriptionShow, PageTable]
types(忽略了)
个人使用感觉:
- 优势
- 传入就用,节约代码开发成本
- 层级少,封装就调用
- 需求更改时只修改配置代码
- 劣势
- 业务不清晰时会频繁修改封装内容
- 提升了新人学习成本
- 提前规划好管理atoms层组件名称、作用、注释
- 需要定期熟悉atoms层内组件