前言
DSL(Domain Specific Language)中文翻译为领域特定语言,例如 SQL、JSON、正则表达式等。与之形成对比的是 GPL(General Purpose Language),中文翻译为通用编程语言,我们熟悉的C、C++、JavaScript、Java 语言等就是。
近几年,前端社区中 DSL 这个词开始频繁出镜,这和环境的变化有很大关系:
React、Vue、Angular等现代框架的表现层设计往往和DSL有较强的关联,透过这些优秀作品我们可以得到一些实践指引。- 前端相关语言的转编译工具链趋于成熟,如
babel,postcss等工具可以帮助开发者以扩展插件的方式低成本地参与到语言构建流程中。 - 社区的解析器生成工具开始普及,如
jison、PEG.js等,可以帮助开发者快速实现全新的编程语言(一般是模板等外部DSL)。 虽然在「术」的实践中我们开始百花齐放,但同时也产生了一些误区或迷思,比如会将DSL和转编译这种纯技术议题划上等号,比如会分不清内部DSL和库(接口)的边界等等,DSL因此成了一个人人都在说但却又很陌生的词汇。
同时市面上的权威著作如 Martin Fowler 的《领域特定语言》虽然会偏向于「道」的解答,但里面充斥着诸如「格兰特小姐的密室控制器」以及「蹦蹦高证券公司」等等对国内前端开发者而言会水土不服的晦涩案例。实际上前端的日常工作已经和 DSL 有着千丝万缕的关系,作为开发者已经不需要通过这些生涩案例来学习 DSL。
特点
DSL 的特点,在《领域特定语言》这本书中的描述,按照我自己的理解为:
是一门便于人们理解的编程语言或规范语言,并且可以被计算机解释执行。 相比于通用编程语言,只能表达有限的逻辑。 因为受限的表达性,所以只会在某一些领域广泛应用。
分类
主要分为三类:外部 DSL、内部 DSL、语言工作台 。
- 内部
DSL是一种通用编程语言的特定用法,只不过具有特定的风格。相对于前端最熟悉的就是jQuery库,还有Grunt、Glup、Mocha等,其都是基于JavaScript语言,在他们所需要解决的领域中形成一定语法风格。
// jQuery
$('#my-div')
.slideDown()
.find('button')
.html('follow')
- 外部
DSL是一种独立的编程语言或规范语言,一般有其特定的语法。前端中除了JavaScript,其实我们大多数使用的都属于外部DSL的范畴,比如HTML、CSS、LESS、JSX等。
<div>
<header>title</header>
<section>content</section>
<footer>footer</footer>
</div>
- 语言工作台 (
Language Workbench)是一种专用的IDE,可以将DSL可视化,并且可以定义和构建一个DSL。前端工程对于IDE和可视化的追求可以说一直是火热,VS code、各种前端搭建可视化工具。
编程语言的发展其实是一个不断抽象的过程,比如从机器语言到汇编语言然后到 C 或 Ruby 这类高级语言:
机器码 --》 汇编 --》 高级(JavaScript)
汇编语言通过助记符代替机器指令操作码,极大的增强了机器语言的可读性和可维护性。但本质上它仍是一门面向处理器和寄存器等硬件系统的低级编程语言。高级语言的出现解决了这个问题,真正脱离了对机器指令集的直接关联,以上层抽象的语句(流程控制、循环等)和数据结构等更趋近自然语言和数学公式的方式完成编码工作,大大提升了程序开发的效率。
但在高级语言层面,抽象带来的效率提升似乎有了天花板。无论是从 C 到 Java,抑或是各种编程范式下衍生的抽象度更高的编程语言,解决的都是通用编程问题,它们都有充分的过程抽象和数据抽象,导致大量的概念产生,进而影响了编程效率。
而在一些专有领域的任务处理上其实不需要那么多语言特性,DSL 就是在这种矛盾中产生的破局方案,它是为了解决特定任务的语言工具,比如文档编写有 markdown,字符串匹配有 RegExp,任务控制有 make、gradle,数据查找有 SQL,Web 样式编码有 CSS 等等。它的本质其实和我们很多软件工程问题的解决思路一样,通过限定问题域边界,从而锁定复杂度,提高编程效率。
UI DSL 的发展
Web 前端并不是只有 HTML/CSS/JS ,其实历史的洪流中还有 Silverlight、Flash、ActiveX 等,然而各种原因互联网选择了 HTML/CSS/JS 。JS 的初衷并不是为了构建复杂 UI 而设计的,所以对于越发复杂的 Web 前端,前端的 UI DSL 也是在循序渐进的发展,HTML/CSS/JS 的规范不断的在改进迭代,业界更是不满足于现在标准而不断的扩展,从 CSS 到 Less/Sass 、 JS/JSX、Vue 和 Angular 都有各自的组件和 Template 实现,再使用转译器,最终运行等效的代码。
通过基于现有语言,扩展或结合创造一种新的 UI DSL,对于前端开发者的接受程度可能会高,但现实原因只可能更复杂,各种标准规范委员会、浏览器厂商、前端 Runtime 等等。小程序则为了提高交互体验结合 Native 和 WebView 的各自优势,UI DSL 趋向于 Web 端语法,对 Web 前端开发人员友好。
相比 Web 端,移动端因为其原生的独特性 UI DSL 则是出现了断崖般的升级。例如 Object-C 到 Swift UI、Android 的 Kotlin Anko、跨平台 Flutter 的 Dart。各语言背后都是大厂商的支持,初衷都是想开发出更好构建 UI 的语言,大家的趋势都是 UI as Code 。
UI as Code
本质上移动端、Web 端虽然有些许不同,但都是前端,问题领域高度一致。UI as Code 在我看来,这个 Code 的意思,就是一门通用编程语言,然后基于这个编程语言来创建一门内部 DSL 来构建 UI ,逻辑和 UI 的编程都是同一门语言,更加高效并减轻开发者的负担。
Web 前端也有类似的发展趋势。CSS 出现了各种 CSS in JS 的方案,JS/HTML 扩展为 JSX,虽然还是有着浓重的 HTML/CSS 色彩。随着现在许多成熟的组件框架库出现,在开发中后台的时候,除了JS ,很多时候就几乎只组装组件和一些简单的布局,已经没有写很多 CSS 和直接操作 DOM。这就感同身受了一句话:“编程就是在为自己的应用程序设计 DSL “ ,就是使用组件针对你开发的应用程序构建内部 DSL(封装组件、方法),通过牺牲一部分通用性灵活性提高开发效率。
Facebook 可以构建出 JSX ,Microsoft 可以创造出 TS ,国内也有 Vue ,相信前端在语言层面上的更大的机遇是会抽象出更加好用的 DSL。
可视化平台
前端搭建系统/可视化平台,难点多难度大,投入风险高,潜在的回报也巨大。大厂商都在投入研发通用性更高的可视化搭建平台,适用范围更广;小厂商则会针对特定业务来开发搭建系统,难度稍低见效快。如果把范围限定在二者之前,在一个适当的业务领域里,研发与业务领域专家充分沟通,投入研发相关的模型和 DSL ,以可视化作为辅助,在灵活性和效率上找到一个平衡点,可以大大提高领域生产力。
我们经常会去尝试创建拖拽加配置的可视化搭建系统来解决各种不同的业务。没有银弹,系统的复杂度不会消失,只会转移,这里就容易产生 COBOL 谬论:”编程语言很难是因为它不像我们平常交流的语言,如果把编程语言设计为像我们平常用的语言,就会容易多了“。"前端很难,我们把它做成可拖拽的可视化平台,就不需要前端开发了"。
antd 相当于是就是构建了基于 JSX的 DSL ,在中后台前端开发领域,通过抽象基础的交互展示逻辑, 牺牲了极少的的可扩展性,通用性和灵活性都达到了一定程度。
可视化搭建系统中有一个难题是,当可视化搭建无法实现某些需求,开发者通过修改代码来实现需求后,之后可能就无法再继续转到平台上。对于代码和可视化搭建的互转是一个难题。如果换一个思路,可视化搭建转成的代码,不是直接的 HTML/CSS/JS,而是一个新的内部 DSL 或外部 DSL ,这个 DSL 背后的模型的在灵活性和代码/可视化的互转上有特定的设计,代码修改,只要符合这个 DSL 的规范,则可以转为可视化的平台中。这就又回到上面的讨论,没有银弹,以 UI as Code 为趋势,在某一方面上牺牲,缩小领域,换取高效。
产生的问题
不管是内部 DSL 或外部 DSL,盲目的创造 DSL ,造成的问题就是,大家学习与理解的负担越来越大,因为使用多种语言比使用一种语言复杂得多,开发可能没有提效反而更加复杂而容易造成 bug 。
但程序的复杂性不会消失,不使用 DSL,也会需要使用不同的程序库,好的程序库的设计就必不可少,可谓 “程序库设计就是语言设计”。然而一个好的 DSL 也应该是让人容易理解并且容易使用,降低学习成本。
结束语
当大家陷入大量业务开发的时候,不防跳出细节,看看一些抽象和理论的东西来找找灵感。随着网络的提速 5G、6G ,还是 AI、AR/VR 的高速发展,前端交互模式的改变和创新,前端可以探索的东西应该是越来越多的。
文章来源: