浅析Vue源码(六)—— $mount中template的编译--generate

1,338 阅读1分钟

上两文章

浅析Vue源码(四)—— $mount中template的编译--parse

浅析Vue源码(五)—— $mount中template的编译--optimize

parse,optimize函数的功能,这里,我们主要介绍generate。

generate 函数主要功能就是根据 AST 结构拼接生成 render function 的字符串。

export function generate (
  ast: ASTElement | void,
  options: CompilerOptions
): CodegenResult {
  const state = new CodegenState(options)
  const code = ast ? genElement(ast, state) : '_c("div")'
  return {
  // 最外层包一个 with(this) 之后返回
    render: `with(this){return ${code}}`,
    // 这个数组中的函数与 VDOM 中的 diff 算法优化相关
    // 我们会在编译阶段给后面不会发生变化的 VNode 节点打上 staticRoot 为 true 的标
    // 那些被标记为 staticRoot 节点的 VNode 就会单独生成 staticRenderFns
    staticRenderFns: state.staticRenderFns
  }
}

其中 genElement 函数是什么呢?--是会根据 AST 的属性调用不同的方法生成字符串返回。也就是拼接字符串了:

export function genElement (el: ASTElement, state: CodegenState): string {
  if (el.staticRoot && !el.staticProcessed) {
    return genStatic(el, state)
  } else if (el.once && !el.onceProcessed) {
    return genOnce(el, state)
  } else if (el.for && !el.forProcessed) {
    return genFor(el, state)
  } else if (el.if && !el.ifProcessed) {
    return genIf(el, state)
  } else if (el.tag === 'template' && !el.slotTarget) {
    return genChildren(el, state) || 'void 0'
  } else if (el.tag === 'slot') {
    return genSlot(el, state)
  } else {
    // component or element
    let code
    if (el.component) {
      code = genComponent(el.component, el, state)
    } else {
      const data = el.plain ? undefined : genData(el, state)

      const children = el.inlineTemplate ? null : genChildren(el, state, true)
      code = `_c('${el.tag}'${
        data ? `,${data}` : '' // data
      }${
        children ? `,${children}` : '' // children
      })`
    }
    // module transforms
    for (let i = 0; i < state.transforms.length; i++) {
      code = state.transforms[i](el, code)
    }
    return code
  }
}

以上就是 compile 函数中三个核心步骤的介绍,compile 之后我们得到了 render function 的字符串形式,后面通过 new Function 得到真正的渲染函数。DOM 初始化过程最后一步是根据渲染函数生成 Vnode,根据此 Vnode 生成真实 DOM,插入 DOM 树中,并将该 Vnode 记录为 preVnode。

要是喜欢就给我一个star,github

感谢muwoo提供的思路。