背景
在阅读公司 react 应用程序时,要从 router 到页面组件到局部组件到 vuex 或者 reduce 到 API 请求,如果代码和依赖关系很多,那么阅读效率就会很低下。
可不可以开发一个工具,把源代码的依赖关系进行可视化展示,来提高阅读效率?
最近无意中接触了 DSL,感觉 DSL 理念可以解决上述问题
DSL 是什么
DSL 其实是 Domain Specific Language 的缩写,中文翻译为领域特定语言(下简称 DSL);而与 DSL 相对的就是 GPL,这里的 GPL 并不是我们知道的开源许可证,而是 General Purpose Language 的简称,即通用编程语言,也就是我们非常熟悉的 Objective-C、Java、Python 以及 C 语言等等。
与 GPL 相对,DSL 与传统意义上的通用编程语言 C、Python 以及 Haskell 完全不同。通用的计算机编程语言是可以用来编写任意计算机程序的,并且能表达任何的可被计算的逻辑,同时也是 图灵完备 的。
但是在里所说的 DSL 并不是图灵完备的,它们的表达能力有限,只是在特定领域解决特定任务的。
另一个世界级软件开发大师 Martin Fowler 对于领域特定语言的定义在笔者看来就更加具体了,DSL 通过在表达能力上做的妥协换取在某一领域内的高效。
而有限的表达能力就成为了 GPL 和 DSL 之间的一条界限。
几个栗子
其实在 DSL 在前端领域已经应用很多了,在这里会举几个例子进行介绍:
- Regex
- 正则表达式仅仅指定了字符串的
pattern,其引擎就会根据pattern判断当前字符串跟正则表达式是否匹配
- 正则表达式仅仅指定了字符串的
- HTML & CSS
HTML和CSS只是对Web界面的结构语义和样式进行描述,虽然它们在构建网站时非常重要,但是它们并非是一种编程语言,正相反,我们可以认为HTML和CSS是在Web中的领域特定语言
- Nuxt.js & Next.js 中的文件即路由模式
- 文件即路由模式,其原理都是生成一个
DSL,其表现形式就是json数据格式或者文件,记录路由和文件的映射关系
- 文件即路由模式,其原理都是生成一个
vue-server-renderer中的vue-ssr-server-bundle.json和vue-ssr-client-manifest.json- 这两个文件其实就是
DSL,里面记录文件的映射关系数据,vue-server-renderer相当于DSL的parser解析器
- 这两个文件其实就是
DSL 特性
上面的几个 🌰 明显的缩小了通用编程语言的概念,但是它们确实在自己领域表现地非常出色,因为这些 DSL 就是根据某一个特定领域的特点塑造的;而通用编程语言相比领域特定语言,在设计时是为了解决更加抽象的问题,而关注点并不只是在某一个领域。
上面的几个例子有着一些共同的特点:
- 没有计算和执行的概念;
- 其本身并不需要直接表示计算;
- 使用时只需要声明规则、事实以及某些元素之间的层级和关系;
虽然了解了 DSL 以及 DSL 的一些特性,但是,到目前为止,我们对于如何构建一个 DSL 仍然不是很清楚。
构建 DSL
DSL 的构建与编程语言其实比较类似,想想我们在重新实现编程语言时,需要做那些事情;实现编程语言的过程可以简化为定义语法与语义,然后实现编译器或者解释器的过程,而 DSL 的实现与它也非常类似,我们也需要对 DSL 进行语法与语义上的设计。
总结下来,实现 DSL 总共有这么两个需要完成的工作:
- 设计语法和语义,定义
DSL中的元素是什么样的,元素代表什么意思 - 实现
parser,对DSL解析,最终通过解释器来执行
以 HTML 为例,HTML 中所有的元素都是包含在尖括号 <> 中的,尖括号中不同的元素代表了不同的标签,而这些标签会被浏览器解析成 DOM 树,再经过一系列的过程调用 Native 的图形 API 进行绘制。

设计原则和妥协
DSL 最大的设计原则就是简单,通过简化语言中的元素,降低使用者的负担;无论是 Regex、SQL 还是 HTML 以及 CSS,其说明文档往往只有几页,非常易于学习和掌握。但是,由此带来的问题就是,DSL 中缺乏抽象的概念,比如:模块化、变量以及方法等。
抽象的概念并不是某个领域所关注的问题,就像
Regex并不需要有模块、变量以及方法等概念。
由于抽象能力的缺乏,在我们的项目规模变得越来越大时,DSL 往往满足不了开发者的需求;我们仍然需要编程语言中的模块化等概念对 DSL 进行补充,以此解决 DSL 并不是真正编程语言的问题。
在当今的 Web 前端项目中,我们在开发大规模项目时往往不会直接手写 CSS 文件,而是会使用 Sass 或者 Less 为 CSS 带来更强大的抽象能力,比如嵌套规则,变量,混合以及继承等特性。
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
}
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
返回到背景中问题
对这个工具可行性分析:
- 对源代码进行 AST 解析,分析出代码之间的依赖关系,以组件为单位;
- 把解析结果转化成
DSL,其表现形式为 json; - 找个可视化插件,进行显示;
上述工具最困难的其实是在第一步,解析的深度问题,直接可以影响结果,目前想到的是以组件为单位,相对简单点,但是针对性比较强