AST--使用编译的方式修改代码
AST–使用编译的方式修改代码最近有一个面板搭建的需求,环境为react,而现有面板组件为vue。就有了现在这个转换需求~AST(抽象语法树):顾名思义,它把代码拆解成了树的形式。接下来我会以代码增删改查的形式带大家初步了解"代码编辑"!查:先看再用!(为什么加后缀,因为觉得内容不够丰满)增:无中生有!(为什么加后缀,因为觉得内容不够丰满)删:罢黜百家!(为什么加后缀,因为觉得...
最近有一个面板搭建的需求,环境为react,而现有面板组件为vue。
就有了现在这个转换需求~
AST(抽象语法树):顾名思义,它把代码拆解成了树的形式。
接下来我会以代码增删改查的形式带大家初步了解"代码编辑"!
- 查:先看再用!(为什么加后缀,因为觉得内容不够丰满)
- 增:无中生有!(为什么加后缀,因为觉得内容不够丰满)
- 删:罢黜百家!(为什么加后缀,因为觉得内容不够丰满)
- 改:偷天换日!(为什么加后缀,因为觉得内容不够丰满)
- 如果你正有vue-to-react需求,那么这是我想说的
一、查:先看再用
我保证要是没有它,你会很痛苦 ast explorer
我们以js代码为例:
- 打开必需品:ast explorer
- 选择JavaScript 和 babylon7
- 输入
const a = 1 + 1
- 右边就会有代码树
也就是拆解到拆不了为止,我们需要的其实就是它的type以及值而已,type就是定义这个数据是什么类型的东西,分了很多类,我们可以在 @babel/types 找到相应的type。
path.skip() 执行之后,就不会在对叶节点进行遍历
path.stop() 执行之后,就会去下次遍历
二、增:无中生有!
现在咋们就来把
const a = 1 + 1
生成出来!为了让新手也能一起动手操练起来,咱们就从新建项目开始。
- 首先我们要创建项目文件夹
- 快速创建package.json
$ npm init -yes
- 下载依赖 :
npm install @babel/parser @babel/types @babel/generator @babel/traverse
- @babel/parser => 字符串代码解析成语法树
- @babel/types => 用来生成和检测数据类型
- @babel/generator => 语法树转成代码字符串
- @babel/traverse => 这次没用到 用来访问语法树,可以在里面进行节点删改
- 项目下创建index.js
- 写入:
const parse = require('@babel/parser').parse;
const t = require('@babel/types');
const generate = require('@babel/generator').default;
// const traverse = require('@babel/traverse').default
const code = '';
const ast = parse(code);
/**
* 便于理解我一步步拆解,这个其实就是拆解代码的逆过程
* 刚接触的话可以根据ast explore中生成的语法树中的type,
* 去到@babel/type中查找相应type,会有相应的方法和入参说明
*/
// 1.生成数字 1
const number = t.numericLiteral(1);
// 2.生成二元表达式 1 + 1
const exp = t.binaryExpression('+', number, number);
// 3.生成变量 a
const varible = t.identifier('a');
// 4.生成变量声明 a = 1 + 1
const declarations = t.variableDeclarator(varible, exp);
// 5.生成变量声明 const a = 1 + 1
const content = t.variableDeclaration('const', [declarations]);
// 将内容放入body中
ast.program.body.push(content);
const output = generate(ast, { quotes: 'single', retainLines: true });
console.log(output.code); // const a = 1 + 1
三、删改:为什么放一起,因为改其实就是先删后增
- 替换:
replaceWith()
replaceWithSourceString()
我们把const a = 1 + 1
改为1 * 1
,这个时候traverse
就用上了
咱们把之前生成的ast
放进来
traverse(ast, {
// 使用相应type来快速访问节点,这里快速来到二元表达式节点,即 1 + 1
// 并使用path.replaceWith() 将节点 1 + 1替换
BinaryExpression(path) {
if (path.node.operator === '+') {
path.replaceWith(t.binaryExpression('*', path.node.left, path.node.right))
}
}
})
tips:对于简单的静态节点还可以直接使用**path.replaceWithSourceString(‘a*b’)**来达到目的!!!
- 插入操作:
pushContainer
、unshiftContainer
、insertBefore
、insertAfter
- pushContainer:针对子节点为数组时,为数组push一个node
const code = `
const obj = {
a: 'a',
b: 'b'
}`;
const ast = parse(code);
const property = t.objectProperty(t.identifier('c'), t.stringLiteral('c'));
traverse(ast, {
ObjectExpression(path) {
path.pushContainer('properties', property);
}
});
- unshiftContainer: 与 pushContainer 对应 =》 unshift
- insertAfter 兄弟节点的插入操作
- 删除节点 path.remove()
四、vue-to-react
vue 有template、script、style 三部分
本人的方案:
style 根据lang 生成 对应style 文件在react文件直接引用
script 解析成使用@babel/parser 生成 ast,操作ast
template 使用vue-template-compiler插件解析,生成ast,根据vue语法树来生成render代码
npm包地址 vue-to-react-tool
本人目前实现的是
- 研究vue组件转react
目前已成功转换 - v-if、v-else-if、v-else
- v-for
- v-show
- v-bind
v-bind:attr.sync = xxx> // 双向绑定的特殊情况
v-bind:attr=xxx
v-on:emiterName ==> emiterName={(new) => this.setState({xxx:new}) - v-model:与v-bind:attr.sync 类似
- v-on
- v-text
=》{{msg}} - v-html => dangerousHtml
- class => className (考虑class v-bind:class同时存在的情况)
- data() => this.state
- Props => props
- {{ expression }} => { expression }
- 组件名转驼峰
- created: ‘componentWillMount’,
- mounted: ‘componentDidMount’,
- updated: ‘componentDidUpdate’,
- beforeDestroy: ‘componentWillUnmount’,
- errorCaptured: ‘componentDidCatch’,
- template => render
- style => index.(css | stylus | sass | less) (目前考虑)
- 移除ts type功能
- this.$refs
- V-for V-if v-show 同时存在的情况
仍需要处理
- 事件修饰符:
- .stop
- .prevent
- .capture
- .self
- .once
- .passive
- v-on:attr = handle => v-on 暂不支持模板字符串型表达式
- v-bind.sync=“doc” => 暂不支持用对象设置多个props
- watch
- Vux / vue-router(目前需求是组件、模块的转换,无需,看后续需求在考虑)
- 。。。
目前是对我们的一个vue组件库进行转换,不过实际的代码情况会更加复杂,开发同学的编码习惯差别也很大,还需要针对各种情况详细处理。同时此方案也可以运用于小程序代码互转等场景中,所以我认为学写一下还是不错的,对代码编译的过程能更加深入了解
更多推荐
所有评论(0)