背景
在阅读公司 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; - 找个可视化插件,进行显示;
上述工具最困难的其实是在第一步,解析的深度问题,直接可以影响结果,目前想到的是以组件为单位,相对简单点,但是针对性比较强