本文内容学习自 哲玄前端 《大前端全栈实践》课程
引言
以后台管理系统为例,很多页面其实都是由菜单(头部菜单,侧边菜单)、搜索栏、数据展示部分(表格)组成。日常的开发中这些相同内容甚至能占到80%。
最普通的开发场景:每次一个新页面,把以前写过的,能用的代码直接cv,然后针对不同的属性来修修改改。每个页面不同的属性就具有不同的代码。
更进阶一点的开发场景:既然每个页面相同块的部分都大差不差,那直接对一个块中的属性进行配置。例如搜索栏的属性就配置一个搜索栏的解析器,表格中的属性就配置一个表格的解析器,不同块的属性就配置一个相应块的解析器。但这样又有一个新的问题:看似封装了一些组件能进行复用,但是当一个新的产品来的时候,又要针对新的产品重新进行类似的封装,即每个产品都需要各自的封装。
由此,我们应该基于数据驱动的方式从数据维度出发,而不是从组件维度看待问题。对于同一份源数据,对数据配置不同的属性来渲染搜索栏,表格等等。而不是划分不同数据来渲染不同的内容。
什么是DSL
DSL全称为Domain-Specific Language (领域特定语言)
DSL 的主要价值在于简化问题的描述和解决方式。设计 DSL 的核心是要体现出意图,而不仅仅是实现功能。通过 DSL,程序员可以将复杂的实现细节隐藏在简洁的语法之下,让使用者专注于表达需求,而非实现细节。
dashboard
以dashboard为例
- DSL都只需要描述数据以及数据相关的配置项,而不用去在意这些配置应该如何的实现。
- 一份DSL模板-> 不同模型(model) -> 不同项目(project)配合解析引擎落地为具体的一个产品
- 不同项目属于同一个模型(
继承实现) - 模型配置描述80%的共同点,项目配置20%的定制化需求
- 图中绿色部分均为
可拓展部分(可定制化部分)
- 图中绿色部分均为
- 不同项目属于同一个模型(
上图中的配置项可以通过以下的描述来进行实现
docs = {
mode: 'dashboard', // 模板类型描述, 不同模板类型对应不一样的数据结构
name: '', // 模板名称
desc: '', // 模板描述
icon: '', // icon
homePage: '', // 首页(项目配置)
menu: [
// menuItem[]
{
key: '', // 菜单唯一描述,
name: '', // 菜单名称
menuType: '', // 菜单类型, 枚举值: 'group' | 'module'
// 1. 当 menuType == group 时, 可填
subMenu: [], // menuItem[],
// 2. 当 menuType == module 时
moduleType: '', // 模块类型, 枚举值: 'schema' | 'iframe' | 'custom' | 'sider'
// 2.1 当 moduleType == schema 时
schemaConfig: {
api: '', // 数据源 API (遵循 RESTFUL 规范)
schema: {
// 模块数据结构
type: 'object',
properties: {
prop: {
...schema, // 标准 schema 结构
type: '', // 字段类型
label: '', // 字段中文名
// 字段在 table 中的相关配置
tableOption: {},
// 字段在 search-bar 中的相关配置
searchOption: {},
},
},
},
// table 相关配置
tableConfig: {
headerButtons: [
{
...elButtonConfig, // 标准 el-button 配置
label: '', // 按钮名称
eventKey: '', // 按钮事件名称
eventOption: {}, // 按钮具体配置
},
],
rowButtons: [
{
...elButtonConfig, // 标准 el-button 配置
label: '', // 按钮名称
eventKey: '', // 按钮事件名称
eventOption: {}, // 按钮具体配置
},
],
},
// search-bar 相关配置
searchConfig: {},
// 模块组件
components: {},
},
// 2.2 当 moduleType == iframe 时
iframeConfig: {
path: '', // iframe 路径
},
// 2.3 当 moduleType == custom 时
customConfig: {
path: '', // 自定义路由路径
},
// 2.4 当 moduleType == sider 时
siderConfig: {
menu: [], // menuItem[], 这里 menuItem 的 moduleType 只能是 'schema' | 'iframe' | 'custom'
},
},
],
}
- 通过解析 menu,即可渲染出对应的菜单,而每个 menuItem 的数据对应其菜单具体要渲染的页面内容。
以 menuItem 里面的 moduleType == schema 为例(即schema-view)
- schemConfig.schema.properties 里面包括我们要渲染的每个数据。
- 通过 tableOption 来控制这个数据在表格中的表现,通过 schemaConfig.tableConfig 来控制整个表格在页面的表现。
- 通过 searchOption 来控制这个数据在表格中的表现,通过 schemaConfig.searchConfig 来控制整个搜索栏在页面的表现。
通过这样的方式完成了用一份源数据在不同的块进行渲染的目标。
- 并不局限于给出的 table 和 search,还可以进行额外的拓展(form, dialog, drawer等等)