菜鸟文章:DSL的设计与实现

各位看官老爷们,今天就让我来分享一下 Elpis全栈开发框架的第三个重要里程碑 DSL设计。 为了更好的理解DSL设计,本文将从一下几个方面来介绍。

  • DSL模板设计是什么
  • 为什么要做DSL设计
  • DSL设计有什么作用
  • elpis项目中是怎么使用DSL的

一、什么是DSL

用专业的话来说,DSL 指的是领域特定语言(Domain-Specific Language),它是为特定领域或特定任务而专门设计的编程语言或标记语言。但作为菜鸟可以将DSL简单理解为一套配置,这套配置可以通过对应的DSL解析器,生成我们所需要的内容(代码,流程,页面等),例如下面这样。

当然只看这个流程图,很多伙伴依然会感觉云里雾里。只有一个简单的流程还是太过抽象,没法让人理解并记忆。所以为了更好的理解这个流程作为菜鸟的我将这个流程做了更细致的图解。各位请看下面这张图,左侧就是我们自定义的配置规则,通过中间自定义的DSL解析器,输出一个右侧的页面。是不是有一点低代码的哪味了😎。

以就是我对DSL的理解,有不对的地方,希望大佬批评指正😁。

为什么要用DSL设计

相信从上面的内容各位小伙伴也能看出,有了DSL之后对于有些页面是不是就不用再让我们发动CV大法去写了,只需要小手一动在配置规则中一配置,页面就可以生成是不是很爽啊😍。而且,各位工作的小伙伴都知道,在我们平时的开发中,有一大部分工作是重复性的像什么crud,前端根据接口参数和ui写页面,后端写crud的接口。写的久了感觉技术上也没什么提升。反倒是CV键磨损有点严重😂。举个例子来说,开发一个电商后台管理系统,但要交给不同的甲方,甲方A需要,功能模块1,功能模块2,功能模块3,功能模块4。但甲方B需要,功能模块1,功能模块2,功能模块3,功能模块5。这个时候功能1到功能3是不是就会重复,我们要么重新写一遍,要么就CV(当然可能有其他解决方案,我这只是举个例子🤣),如下图的示例。这个时候我们就可以用DSL的方式将重复的三个模块沉淀下来,只需要关注不同的模块即可,这样可以大大提高我们的开发效率,也能提高我们的开发水平。

这个时候有小伙伴就要问了,万一沉淀下来的模块也需要有一点个性化的内容怎么办,这时候就要祭出下面这张图了😎。从这张图中就可以看出我们深蓝色的部分就是要沉淀的内容。但沉淀的内容并不是完全固定不变的,我们在设计的时候就预留了一些插槽供开发者进行拓展(图中绿色的部分)。

以上就是我们使用DSL的原因,总的来说就是一句话,把体力活交给解析器从而专注与新功能模块的开发。

DSL设计的作用

主要作用在上述内容中就已经提到了,减少重复性工作,提高代码的可维护性。

DSL在elpis项目中的应用

接下来就是我们的重中之重🧐。下面给大家展示一下整个项目关于DSL的设计,这时候有一个问题,既然我们使用DSL设计是为了沉淀重复性工作,更好的支持多方交付。难道每一次开发系统都要写一遍配置吗😱?答案是当然不用。z这时候就要引入一个面向对象的理念。从下面这个图可以看出,我们每一个DSL可以有有不同的模型例如电商,课程系统等,在每一个模型中我们可以将重复性的内容抽离到一个基类当中,对于模型下每一个不同的项目我们可以通过继承的方式获取沉淀的内容,项目的模块会重载(项目配置覆盖基类的配置)这样可以进一步提升效率

具体的继承过程如下。

接下来会展示我们具体的DSL配置规则,这其中都有清晰的注释。通过下面两张图各位小伙伴应该能理解我们的DSL配置规则。

go 复制代码
{
    mode:'dashboard' //模板类型,不同模板类型对应不同的模板数据结构
    name:'' //名称
    desc:'' //描述
    icon:'' //icon
    homePage:'' //首页(项目配置)

    //头部菜单
    menu:[{
        key:'', //菜单唯一描述
        name:'', //菜单名称
        menuType:'', //枚举值,group / module
        
        //当menuType == group 时, 可填
        subMenu:[{
            //可递归 menuItem
        },],

        //当menuType == module 时, 可填
        moduleType:'', //枚举值 :sider/iframe / custom / schema

        //当 moduleType == sider时
        siderConfig:{
            menu:[{
                //可递归menuItem(除 moduleType === sider)
            }]
        },

        //当 moduleType == iframe时
        iframeConfig:{
            path:'', //iframe 路径
        },

        //当 moduleType == custom时
        customConfig:{
            path:'', //自定义路由路径

        },

        //当 moduleType == schema时
        schemaConfig:{
            api:'', //数据源API (遵循 RESTFUL 规范)
            schema:{
                //板块数据结构
                type:'object',
                properties:{
                    key:{
                        ...schema, //继承标准 schema 配置
                        type:'', //字段类型
                        label:'', //字段的中文名
                        //字段在table中的相关配置
                        tableOption:{
                            ...elTableColumConfig, //标准el-table-column 配置
                            toFixed:0, //暴露小数点后几位
                            visiable:true //默认为true(false 表示不再表单中显示)
                        },
                        //字段在search中的配置
                        searchOption:{
                            ...eleComponentConfig,//标准el-component-column
                            comType:'', //配置组件类型 input/select/...
                            default:'', //默认值

                            //当comType === select
                            enumList:[], //下拉框可选

                            //当comType === 'dynamicSelect'时
                            api:''

                        }
                    }
                },
            },
            //table 相关配置
            tableConfig:{
                headerButtons:[{
                    label:'', //按钮中文名
                    eventKey:'', //按钮事件名
                    eventOptions:{}, //按钮具体配置
                    ...elButtonConfig //标准el-button配置
                }],
                rowButtons:[{
                    label:'', //按钮中文名
                    eventKey:'', //按钮事件名
                    eventOptions:{
                        //当eventKey === 'remove'等
                        parmas:{
                            //paramKey = 参数的健值
                            //rowValueKey = 参数值  (当格式为 schema::tableKey 的时候,到 table 中找到响应的字段)
                            parmaKey:rowValueKey
                        }
                    }, //按钮事件具体配置
                    ...elButtonConfig //标准el-button配置
                }]
            }, //table相关配置
            searchConfig:{}, //search-bar 相关配置
            components:{}, //模块组件
        },
    }]
}

今天主要想介绍的是我们的schemaConfig

go 复制代码
 //当 moduleType == schema时
        schemaConfig:{
            api:'', //数据源API (遵循 RESTFUL 规范)
            schema:{
                //板块数据结构
                type:'object',
                properties:{
                    key:{
                        ...schema, //继承标准 schema 配置
                        type:'', //字段类型
                        label:'', //字段的中文名
                        //字段在table中的相关配置
                        tableOption:{
                            ...elTableColumConfig, //标准el-table-column 配置
                            toFixed:0, //暴露小数点后几位
                            visiable:true //默认为true(false 表示不再表单中显示)
                        },
                        //字段在search中的配置
                        searchOption:{
                            ...eleComponentConfig,//标准el-component-column
                            comType:'', //配置组件类型 input/select/...
                            default:'', //默认值

                            //当comType === select
                            enumList:[], //下拉框可选

                            //当comType === 'dynamicSelect'时
                            api:''

                        }
                    }
                },
            },
            //table 相关配置
            tableConfig:{
                headerButtons:[{
                    label:'', //按钮中文名
                    eventKey:'', //按钮事件名
                    eventOptions:{}, //按钮具体配置
                    ...elButtonConfig //标准el-button配置
                }],
                rowButtons:[{
                    label:'', //按钮中文名
                    eventKey:'', //按钮事件名
                    eventOptions:{
                        //当eventKey === 'remove'等
                        parmas:{
                            //paramKey = 参数的健值
                            //rowValueKey = 参数值  (当格式为 schema::tableKey 的时候,到 table 中找到响应的字段)
                            parmaKey:rowValueKey
                        }
                    }, //按钮事件具体配置
                    ...elButtonConfig //标准el-button配置
                }]
            }, //table相关配置
            searchConfig:{}, //search-bar 相关配置
            components:{}, //模块组件
        },

上图是为了让小伙伴们了解schemaConfig的具体内容是什么。接下来我将介绍一下用schema去沉淀重复性工作的优点是什么。首先在传统的开发模式中我们大多数情况是面向ui编程,根据设计稿和接口文档开发页面。这时候就难以形成规范,可能造成同一个工能,在不同的产品手中就会有不同的展示结果,这大大增加了我们的工作量。

而使用DSL之后输出对应的DSL解析器和配置规则以后,就可得到我们的目标内容,使重复性内容得到规范,并且可以提高,产品-ui-前端-后端,整个流程的一个开发效率

以上就是关于DSL设计的全部内容,仅代表个人观点,望各位大佬包含😎。

出处:《哲玄课堂-大前端全栈实践》

相关推荐
AI大模型-小华19 分钟前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子3 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅3 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni3 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学4 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师5 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc885 小时前
微软EDGE浏览器功能学习
前端·学习·edge
Csvn5 小时前
🎯 原生 `<dialog>` 元素:终于可以扔掉一半的自定义弹窗组件了?
前端
Csvn5 小时前
🎨 CSS @layer:用「层叠优先级」终结样式打架的世纪难题
前端