首先我们介绍一下本文的关键点:抽象语法树,它是以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
通过操作这棵树,可以精确的定位到声明、赋值、运算语句,从而实现对代码的优化、变更等操作。
本文通过对 VUE 组件的 JavaScript 、CSS模块进行转换,比如 JavaScript模块,包括外层对象,生命周期钩子函数,赋值语句,组件的内部数据,组件的对外属性等等来实现把一个 VUE 组件转换为 一个小程序组件。
AST 抽象语法树,似乎我们平时并不会接触到。实际上在我们的项目当中,CSS 预处理,JSX 亦或是 TypeScript 的处理,代码格式化美化工具,Eslint, Javascript 转译,代码压缩,Webpack, Vue-Cli,ES6 转 ES5,当中都离不开 AST 抽象语法树这个绿巨人。先卖个关子,让我们看一下 AST 到的官方解释:
It is a hierarchical program representation that presents source code structure according to the grammar of a programming language, each AST node corresponds to an item of a source code.
中文的解释有:
抽象语法树(abstract syntax tree或者缩写为 AST ),或者语法树(syntax tree),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。和抽象语法树相对的是具体语法树(concrete syntaxtree),通常称作分析树(parse tree)。一般的,在源代码的翻译和编译过程中,语法分析器创建出分析树。一旦 AST 被创建出来,在后续的处理过程中,比如语义分析阶段,会添加一些信息。
抽象语法树,它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
通过操作这棵树,可以精确的定位到声明、赋值、运算语句,从而实现对代码的优化、变更等操作。这些并不是我们想要看到的。
对于 AST 面纱的神秘感,似乎已经将要揭开。不错,在我刚接触到他的时候,同样感觉确实是难。但是当你开始了解了它以后,你就会越来越喜欢它。
因为他实在太强大了。AST 本身并不难,难点在于转换的目标对象与源对象的语法差异,当中水深毋庸置疑。但是,这才能更加激起我们探索他的欲望。在开始之前,我们先看一下 抽象语法树到底长什么样子。
一、 一探究竟 AST
通过 astexplorer [1] (AST树查看网站),通过他你可以方便的查看代码的语法树,挑你喜欢的库。你可以在线把玩 AST,而且除了 JavaScript,HTML, CSS 还有很多其它语言的 AST 库,让我们对他有一个感性而直观的认识。
请看下图,看看AST语法树长什么样子:
此图看到的是一个 ExportDefaultDeclaration 也就是export default {},还有他的位置信息,注释失信,tokens等等。
国际惯例,先来一个小demo
输入数据
function square(n) { return n * n; }
处理数据
astFn() { const code = `function square(n) { return n * n; }`; //得到 AST 语法树 const ast = babylon.parse(code); traverse(ast, { enter(path) { console.log("enter: " + path.node.type); //如下的语句匹配到 return 中的 n 与参数 n,并且将它替换为 x。 if (path.node.type === "Identifier" && path.node.name === "n") { path.node.name = "x"; } } }); generate(ast, {}, code);//将 AST 转换为代码 console.log(generate(ast, {}, code).code );//打印出转换后的 JavaScript 代码 }
输出数据
function square(x) { return x * x; }
我们看一下我们得到的 AST 树
接下来我们插入一段 把 VUE 组件转换为微信小程序组件正则版本的处理
二、 简单粗暴的版本(VUE 组件转换为微信小程序组件)
没有使用 AST 将 VUE 组件转换成小程序组件的简易版本介绍
下方是两段代码,简单的逻辑,实现思路,匹配目标字符串,替换字符,然后生成文件。
regs:{//通过标签匹配来替换对应的小程序支持的标签 toViewStartTags: /(<h1)|(<s)|(<em)|(<ul)|(<li)|(<dl)|(<i)|(<span)/g, toViewEndTags: /(<\/h1>)|(<\/s>)|(<\/em>)|(<\/ul>)|(<\/li>)|(<\/dl>)|(<\/i>)|(<\/span>)/g, toBlockStartTags: /(<div)|(<p)/g, toBlockEndTags: /(<\/div>)|(<\/p>)/g, }, signObj: {//通过标签查找来分离脚本,模板和CSS tempStart: '<template>', tempEnd: '</template>', styleStart: '<style scoped>', styleEnd: '</style>', scriptStart: '<script>', scriptEnd: '</script>' }
上方是正则版本的一些模板匹配规则,经过后续的一系列处理把一个 VUE组件处理得到对应的小程序的 WXML ,WXSS,JSON,JS,4个文件。