//ExportDefault 到 Component构造器的转换 ExportDefaultDeclaration(path) { //创建 CallExpression Component({}) function insertBeforeFn(path) { const objectExpression = t.objectExpression(propertiesAST); test = t.expressionStatement( t.callExpression(//创建名为 Compontents 的调用表达式,参数为 objectExpression t.identifier("Compontents"),[ objectExpression ] ) ); //最终得到的语法树 console.log("test",test) } if (path.node.type === "ExportDefaultDeclaration") { if (path.node.declaration.properties) { //提取属性并存储 propertiesAST = path.node.declaration.properties; //创建 AST 包裹对象 insertBeforeFn(path); } //得到我们最终的转换结果 console.log(generate(test, {}, code).code);
对于 ExportDefault => Component 构造器转换还有一种转换思路 下面我们看一下:
[1] 第一种思路是先提取 ExportDefault 内部所有节点的 AST ,并做处理,然后创建Component构造器,插入提取处理后的 AST,得到最终的 AST
//propertiesAST 这个就是我们拿到的 AST,然后在对应的分支内做对应的处理 以下分别为 data,methods,props,其他的钩子同样处理即可
propertiesAST.map((item, index) => {
if (item.type === "ObjectProperty") {
//props 替换为 properties
if (item.key.name === "props") {
item.key.name = "properties";
}
} else if (item.type === "ObjectMethod") {
if (path.node.key.name === "mounted") {
path.node.key.name = "ready";
} else if (path.node.key.name === "created") {
path.node.key.name = "attached";
} else if (path.node.key.name === "destroyed") {
path.node.key.name = "detached";
} else if (path.node.type === "ThisExpression") {
if (path.parent.property.name === "$emit") {
path.parent.property.name = "triggerEvent";
}
} else {
void null;
}
}
} else if (path.node.key.name === "methods") {
path.traverse({
enter(path) {
if (path.node.type === "ThisExpression") {
if (path.parent.property.name === "$emit") {
path.parent.property.name = "triggerEvent";
}
}
}
})
}
else {
//...
console.log("node type", item.type);
}
});
[2] 第二种思路呢,就是我们上面展示的这种,不过有一个关键的地方要注意一下:
//我把 ExportDefaultDeclaration 的处理放到最后来执行,拿到 AST 首先进行转换。然后在创建得到新的小程序组件JS部分的 AST 即可 traverse(ast, { enter(path) {}, ObjectProperty(path) {}, ObjectMethod(path) {}, //...... ExportDefaultDeclaration(path) { //... } })
如果你想在 AST 开始处与结尾处插入,可使用 path 操作:
path.insertBefore( t.expressionStatement(t.stringLiteral("start..")) ); path.insertAfter( t.expressionStatement(t.stringLiteral("end..")) );
注:关于微信小程序不支持 computed , 与 watch,我们具体的初期采用的方案是挂载 computed 和 watch 方法到每一个微信小程序组件,让小程序组件也支持这两个功能。
六,VUE 组件转换为微信小程序组件中 的 Data 部分的处理:
关于 Data 部分的处理实际上就是:函数表达式转换为对象表达式 (FunctionExpression 转换为 ObjectExpression)
转换之前的 JavaScript 代码
export default { data(){//函数表达式 return { num: 10000, arr: [1, 2, 3], obj: { d1: "val1", d2: "val2" } } } }
处理后我们得到的
export default {
data: {//对象表达式
num: 10000,
arr: [1, 2, 3],
obj: {
d1: "val1",
d2: "val2"
}
}
};
通过如上的代码对比,我们看到了我们的转换前后代码的变化:
转换前后 AST 树对比图明确转换目标:
我们对 JavaScript 动了什么手脚(亦可封装成babel插件):
const ast = babylon.parse(code, { sourceType: "module", plugins: ["flow"] }); //AST 转换node、nodetype很关键 traverse(ast, { enter(path) { //打印出node.type console.log("enter: " + path.node.type); } })
我们的转换部分都尽量在一个 Traverse 中处理,减少 AST 树遍历的性能消耗