返回

手写Vue源码——代码生成器

前端

在前面的学习vue源码(6)熟悉模板编译原理中,我们谈到了,模板编译分为解析器,优化器,代码生成器。 前面两部分已经实现,现在 就来看看 代码生成器怎么实现吧。

代码生成器的作用

代码生成器的作用是使用 AST 生成 render 函数代码字符串。 生成后的代码字符串中看到了有几个函数调用 _c,_v…

手写一个简单的代码生成器

  /**
   * codegen AST 对象,转换成可执行 render 函数字符串
   * @param {*} ast 
   */
  function generate (ast) {
    const context = createCodegenContext()
    const code = genElement(ast, context)
    return {
      render: code.render
    }
  }

  /**
   * 创建代码生成器的上下文
   */
  function createCodegenContext () {
    const context = {
      // code
      code: '',
      // 当前渲染函数的行号
      line: 1,
      // 当前渲染函数的列号
      column: 1,
      // 优化标记,用来提升 render 函数代码质量
      optimize: true,
      // 在生成变量名称之前,保存变量的序列号,减少变量名之间的重复
      varSeq: 0,
      // 在代码中保存变量名,避免重复声明
      saved: []
    }
    return context
  }

  /**
   * 生成可执行的 render 函数字符串
   * @param {*} elementAst 节点 AST
   * @param {*} context 上下文环境
   */
  function genElement (elementAst, context) {
    const {children, tag, attrs} = elementAst
    // 开头生成 _c 函数调用,调用形式 _c('标签',属性,孩子...)
    const code = `_c('${tag}', ${
      attrs.length ? genProps(attrs) : 'undefined'
    }${
      children.length ? `,${genChildren(children, context)}` : ''
    })`
    return {
      render: code
    }
  }

上面的 genElement 方法处理了最基本的一个 DOM 节点的渲染函数生成逻辑。 在 generate 方法中,我们创建了一个 codegen 上下文对象,然后调用 genElement 方法来生成 render 函数代码字符串。

完整版本的代码生成器可以生成一个完整的 render 函数代码字符串, 包括对动态数据、事件和插槽的支持。