[Vue] Generate JS code based on JSAST

通过前面的代码,其实我们已经完成了从模板ASTJavascript AST的转换

const ast = parse(template);
transform(ast);

那接下来,我们只需要再根据Javascript AST生成具体的js代码就行了,比如再执行下面的函数:

generate(ast.jsnode)

因为我们之前是把JSAST存储在jsnode对象中的。

而这个generate函数,无非也就是拼字符串,根据JSAST中不同的类型,进行不同的字符串拼接就行,当然,还需要注意代码格式。

因此,为了更好的处理代码拼接,我们还是用变量将要拼接的字符串缓存处理,同时为了处理代码格式,我们还是依照之前的处理方式,使用一个上下文对象方便维护代码生成过程中的运行状态:

function generate(node){
  const context = {
    // 最终生成的渲染代码字符串
    code: '',
    // 通过调用push函数完成代码拼接
    push(code){
      context.code += code;
    },
    // 当前缩进级别,初始值为0,表示没有缩进
    currentIndent: 0,
    // 用来换行,并且换行后的代码会增加缩进
    newline(){
      context.push('\n' + ' '.repeat(context.currentIndent));
    },
    // 缩进函数,让currentIndent自增后,调用newline函数
    indent(){
      context.currentIndent ++;
      context.newline();
    },
    // 取消缩进函数,让currentIndent自减后,调用newline函数
    deIndent(){
      context.currentIndent --;
      context.newline();
    }
  }

  // 调用genNode完成代码生成工作
  genNode(node, context);

  // 返回渲染函数代码
  return context.code;
}

所以下面的主要问题,就是genNode函数的编写,那也就是根据我们之前JSAST的几种状态去拼接字符串,我们现在直接拼接给context.code就行了

function genNode(node, context) {
  switch (node.type) {
    case "FunctionDecl":
      // 生成函数声明代码
      genFunctionDecl(node, context);
      break;
    case "ReturnStatement":
      // 生成return语句代码
      genReturnStatement(node, context);
      break;
    case "CallExpression":
      // 生成函数调用代码
      genCallExpression(node, context);
      break;
    case "StringLiteral":
      // 生成字符串字面量代码
      genStringLiteral(node, context);
      break;
    case "ArrayExpression":
      // 生成数组字面量代码
      genArrayExpression(node, context);
      break;
  }
}

下面就是依次根据类型来拼接字符串

function genFunctionDecl(node, context) {
  // 从context中取出够函数
  const { push, indent, deIndent } = context;

  // 拼接函数名
  push(`function ${node.id.name} `);
  push(`(`);
  // 生成函数参数列表
  genNodeList(node.params, context);
  push(`) `);
  push(`{`);
  // 缩进
  indent();

  // 生成函数体,这里只需要递归调用genNode函数即可
  node.body.forEach((n) => genNode(n, context));

  // 取消缩进
  deIndent();
  push(`}`);
}

function genNodeList(nodes, context) {
  nodes.forEach((node, index) => {
    genNode(node, context);
    if (index < nodes.length - 1) {
      context.push(`, `);
    }
  });
}

function genReturnStatement(node, context) {
  const { push } = context;

  push(`return `);
  genNode(node.return, context);
}

function genCallExpression(node, context) {
  const { push } = context;
  const { callee, arguments: args } = node;
  push(`${callee.name}(`);
  genNodeList(args, context);
  push(`)`);
}

function genStringLiteral(node, context) {
  const { push } = context;

  push(`'${node.value}'`);
}

function genArrayExpression(node, context) {
  const { push } = context;
  push("[");
  genNodeList(node.elements, context);
  push("]");
}

const ast = parse(template);
transform(ast);

console.log(generate(ast.jsNode));
posted @ 2025-06-27 13:41  Zhentiw  阅读(12)  评论(0)    收藏  举报