内容来源于抖音【哲玄前端】大佬的《大前端全栈实践》课程,此课程是从零开始做一个企业级的全栈应用框架。
1.什么是dsl?
看到dsl我一脸的😳,这是什么东西? 打死了? 带伞了?剁手了?,不懂就AI,以下是豆包详解:(反正比我自己解释的牛逼,有问题也别找我)
DSL(Domain-Specific Language,领域特定语言) 是一种专为解决特定领域问题而设计的计算机语言,它聚焦于某个特定领域的需求,通过简化语法、抽象概念和提供领域专属的表达方式,让开发者能更高效地解决该领域的特定问题。
DSL 的核心特点
-
领域针对性
- 仅针对某个特定领域(如数学建模、网页设计、配置管理、数据查询等),而非通用编程。
-
语法简洁性
- 语法通常比通用语言(如 Java、Python)更简单、直观,甚至接近自然语言,降低学习和使用成本。
-
抽象与封装
- 隐藏领域内的底层细节,通过高层抽象提供领域专属的操作符或关键词。
- 例:CSS 中
font-size: 16px; color: red;直接定义字体样式,无需关心浏览器渲染的底层实现。
-
执行方式灵活
- 可通过解释器(如 SQL 由数据库引擎解析执行)或编译器(如某些配置 DSL 编译为二进制代码)实现。
- 部分 DSL 可嵌入通用语言中(如 Python 的
Django ORM作为 DSL 操作数据库)。
2.基于dsl我们做了什么
2.1 基于json-schema 定义基础配置
主要是定义未来我们需要解释的基本数据结构,通过约定俗成的结构配置基于后部分的解释器或者说是编译模块对数据的抽离和基于数据的组件渲染组件渲染 (以下是我的具体文档)
{
mode : 'dashboard',
name: '', // 名称
desc: '', // 描述
icon: '', // 图标
homePage: '', // 首页
// 头部菜单
menu : [{
key: '', // 菜单key
name: '', // 菜单名称
menuType: '', // 菜单类型 枚举值 group/model
// menuType为group时,可填
subMenu: [
{},{} // 可递归item
],
moduleType: '', // 枚举值 1.iframe/custom/schema/slider
// 当moduleType为slider时,可填
sliderConfig: {
menu: [
// 可递归item(除了module Type==slider)
]
},
// 当moduleType为iframe时,可填
iframeConfig: {
path: '' // iframe路径
},
// 当moduleType为custom时,可填
customConfig: {
path: '' // 自定义路由路径
},
// 当moduleType为schema时,可填
schemaConfig: {
api: '', // 数据源api(restFull)
schema: {
type: 'object',
properties: {
// 属性数据结构
key: {
...schema, // 标准schema结构
type: 'string', // 字段类型
label: '', // 字段中文名
// 字段在table中的相关配置
tableOption: {
...elTableColumnConfig, // 标准elTableColumn结构
toFixed: 0, // 保留小数点后几位
visible: true, // 是否显示
},
// 字段在search-bar中的相关配置
searchOption: {
...eleComponentConfig, // 标准elComponent结构
visible: true, // 是否显示
comType: '', // 组件类型
default: '', // 默认值
// comType 为select时
enumList: [] // 下拉框内容
// conType 为dynamicSelect时
// 动态下拉框内容
api: ''
}
}
}
}, // 板块数据结构
tableConfig: {
// 表格配置
headerButtons: [{
label: '', // 按钮名称
eventKey: '', // 事件名称
eventOption: {}, // 按钮事件配置
...elButtonConfig, // 标准elButton配置
}, ...],
rowButtons: [{
label: '', // 按钮名称
eventKey: '', // 事件名称
eventOption: {
// 当eventKey为remove时
params : {
// paramKey = 参数键值
// rowValueKey = 参数值(当格式为schema:tableKey的时候,到table中招响应的字段)
paramKey: rowValueKey
}
}, // 按钮事件配置
...elButtonConfig, // 标准elButton配置
}, ...],
},
searchConfig: {
// 搜索配置
columns: [
// 搜索列配置
]
},
components: {} // 组件配置
}
},
}
2.2 总体设计图 (我偷偷拍的)
3.目前的核心模块
3.1 dashboard
定义前端基础渲染模板基础页面的布局,相当于这个项目基础骨架
3.2 headerview
在 dashboard顶部定义的组件提供主菜单渲染逻辑,及路由跳转能力,及右侧插槽提高可拓展性
3.3 基于各种不同展示页面类型提供渲染组件
3.3.1 iframe
嵌入iframe 的一个组件, 提供项目需要链接外部页面的能力
3.3.2 schema (重点来了)
用2中提供的各个schemaConfig 配置 进行对应组件渲染,目前主要实现了列表页的渲染,查询删除功能, 核心设计思路我认为是抽离对应配置比如,我需要渲染列表,此时我应该抽取tableOption,tableConfig作为我渲染的主要数据,应该怎么做呢? 先读取对应页面配置,通过递归找到对应页面的配置项,但是如上面我可能有不同渲染组件的配置项(比如table, search),所以需要一个公共方法来抽离抽出对应的option和Config
{
if (!_schema?.properties) {
return {};
}
const dtoSchema = {
type: "object",
properties: {},
};
// 提取有效schema字段信息
for (const key in _schema.properties) {
const props = _schema.properties[key];
if (props[`${comName}Option`]) {
let dtoProps = {};
for (const pKey in props) {
if (pKey.indexOf("Option") < 0) {
dtoProps[pKey] = props[pKey];
}
}
// 处理comName Option
dtoProps = Object.assign({}, dtoProps, {
option: props[`${comName}Option`],
});
dtoSchema.properties[key] = dtoProps;
}
}
return dtoSchema;
};
这样就好了!
关于search组件中我觉得很好的设计在于 建立 对应组件的枚举对象再用component内置组件进行渲染, SearchItemConfig其实就是
SearchItemConfig = {
[组件类型名称] = {
cpmponent: 引入的对应组件
}
}
<component
:is="SearchItemConfig[schemaItem.option?.comType]?.component"
/ >
3.3.3 sider
带有侧边栏的页面容器
3.3.4 custom
之前的这些模块实际已经覆盖了大多数业务场景,其余需要我们自己补充的可以在custom 组件中进行自己开发