genAssets 生成组件 指令 过滤器

function genAssets(
  assets: string[],
  type: 'component' | 'directive' | 'filter',
  { helper, push, newline }: CodegenContext
) {
  // 调用对应的函数进行解析
  const resolver = helper(
    __COMPAT__ && type === 'filter'
      ? RESOLVE_FILTER
      : type === 'component'
        ? RESOLVE_COMPONENT
        : RESOLVE_DIRECTIVE
  )
  for (let i = 0; i < assets.length; i++) {
    let id = assets[i]
    // potential component implicit self-reference inferred from SFC filename
    const maybeSelfReference = id.endsWith('__self')
    if (maybeSelfReference) {
      id = id.slice(0, -6)
    }
    push(
      `const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${
        maybeSelfReference ? `, true` : ``
      })`
    )
    if (i < assets.length - 1) {
      newline()
    }
  }
}

genVNodeCall 生成VNode

// 生成vnode 用于调用createVnode创建节点渲染
function genVNodeCall(node: VNodeCall, context: CodegenContext) {
  const { push, helper, pure } = context
  const {
    tag,
    props,
    children,
    patchFlag,
    dynamicProps,
    directives,
    isBlock,
    disableTracking
  } = node
  if (directives) {
    push(helper(WITH_DIRECTIVES) + `(`)
  }
  // 如果是block push一个_openBlock(
  if (isBlock) {
    push(`(${helper(OPEN_BLOCK)}(${disableTracking ? `true` : ``}), `)
  }
  if (pure) {
    push(PURE_ANNOTATION)
  }
  // 如果是block 就创建一个_createBlick 否则是createVnode
  push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + `(`, node)
  genNodeList(
    genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
    context
  )
  push(`)`)
  if (isBlock) {
    push(`)`)
  }
  // 生成指令
  if (directives) {
    push(`, `)
    genNode(directives, context)
    push(`)`)
  }
}

genNode 生成Node

function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
  if (isString(node)) {
    context.push(node)
    return
  }
  if (isSymbol(node)) {
    context.push(context.helper(node))
    return
  }
  switch (node.type) {
    case NodeTypes.ELEMENT: // 1 生成元素,然后调用genVnodeCall 进行生成
    case NodeTypes.IF: // 9
    case NodeTypes.FOR: // 11
      __DEV__ &&
        assert(
          node.codegenNode != null,
          `Codegen node is missing for element/if/for node. ` +
            `Apply appropriate transforms first.`
        )
      genNode(node.codegenNode!, context)
      break
    case NodeTypes.TEXT: // 2
      genText(node, context)
      break
    case NodeTypes.SIMPLE_EXPRESSION: // 4 生成表达式
      genExpression(node, context)
      break
    case NodeTypes.INTERPOLATION: // 5 生成文本 例如:{{ count }}
      genInterpolation(node, context)
      break
    case NodeTypes.TEXT_CALL: // 12
      genNode(node.codegenNode, context)
      break
    case NodeTypes.COMPOUND_EXPRESSION: // 8
      genCompoundExpression(node, context)
      break
    case NodeTypes.COMMENT: // 3
      genComment(node, context)
      break
    case NodeTypes.VNODE_CALL: // 13 具体生成元素的部分
      genVNodeCall(node, context)
      break

    case NodeTypes.JS_CALL_EXPRESSION: // 14
      genCallExpression(node, context)
      break
    case NodeTypes.JS_OBJECT_EXPRESSION: // 15 生成属性 如class style
      genObjectExpression(node, context)
      break
    case NodeTypes.JS_ARRAY_EXPRESSION: // 17
      genArrayExpression(node, context)
      break
    case NodeTypes.JS_FUNCTION_EXPRESSION: // 18 生成函数语句
      genFunctionExpression(node, context)
      break
    case NodeTypes.JS_CONDITIONAL_EXPRESSION: // 19
      genConditionalExpression(node, context)
      break
    case NodeTypes.JS_CACHE_EXPRESSION: // 20
      genCacheExpression(node, context)
      break

    // SSR only types
    case NodeTypes.JS_BLOCK_STATEMENT: // 21
      !__BROWSER__ && genNodeList(node.body, context, true, false)
      break
    case NodeTypes.JS_TEMPLATE_LITERAL: // 22
      !__BROWSER__ && genTemplateLiteral(node, context)
      break
    case NodeTypes.JS_IF_STATEMENT: // 23 生成if表达式
      !__BROWSER__ && genIfStatement(node, context)
      break
    case NodeTypes.JS_ASSIGNMENT_EXPRESSION: // 24
      !__BROWSER__ && genAssignmentExpression(node, context)
      break
    case NodeTypes.JS_SEQUENCE_EXPRESSION: // 25
      !__BROWSER__ && genSequenceExpression(node, context)
      break
    case NodeTypes.JS_RETURN_STATEMENT: // 26
      !__BROWSER__ && genReturnStatement(node, context)
      break

    /* istanbul ignore next */
    case NodeTypes.IF_BRANCH: // 10
      // noop
      break
    default:
      if (__DEV__) {
        assert(false, `unhandled codegen node type: ${(node as any).type}`)
        // make sure we exhaust all possible types
        const exhaustiveCheck: never = node
        return exhaustiveCheck
      }
  }
}

genNodeList 生成NodeList

function genNodeList(
  nodes: (string | symbol | CodegenNode | TemplateChildNode[])[],
  context: CodegenContext,
  multilines: boolean = false,
  comma: boolean = true
) {
  const { push, newline } = context
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i]
    if (isString(node)) {
      push(node)
    } else if (isArray(node)) {
      // 如果是数组就生成多个
      genNodeListAsArray(node, context)
    } else {
      // 生成一个
      genNode(node, context)
    }
    if (i < nodes.length - 1) {
      // 如果是多个,就添加一个,号,并在添加一行
      if (multilines) {
        comma && push(',')
        newline()
      } else {
        // 否则只添加,
        comma && push(', ')
      }
    }
  }
}

genHoists 生成静态提升的节点

一个静态提升的json

[
    {
        "type": 13,
        "tag": "\"div\"",
        "props": {
            "type": 15,
            "loc": {
                "start": {
                    "column": 13,
                    "line": 12,
                    "offset": 406
                },
                "end": {
                    "column": 47,
                    "line": 12,
                    "offset": 440
                },
                "source": "<div id=\"suspense\">supense组件</div>"
            },
            "properties": [
                {
                    "type": 16,
                    "loc": {
                        "source": "",
                        "start": {
                            "line": 1,
                            "column": 1,
                            "offset": 0
                        },
                        "end": {
                            "line": 1,
                            "column": 1,
                            "offset": 0
                        }
                    },
                    "key": {
                        "type": 4,
                        "loc": {
                            "source": "id",
                            "start": {
                                "column": 18,
                                "line": 12,
                                "offset": 411
                            },
                            "end": {
                                "column": 20,
                                "line": 12,
                                "offset": 413
                            }
                        },
                        "content": "id",
                        "isStatic": true,
                        "constType": 3
                    },
                    "value": {
                        "type": 4,
                        "loc": {
                            "start": {
                                "column": 21,
                                "line": 12,
                                "offset": 414
                            },
                            "end": {
                                "column": 31,
                                "line": 12,
                                "offset": 424
                            },
                            "source": "\"suspense\""
                        },
                        "content": "suspense",
                        "isStatic": true,
                        "constType": 3
                    }
                }
            ]
        },
        "children": {
            "type": 2,
            "content": "supense组件",
            "loc": {
                "start": {
                    "column": 32,
                    "line": 12,
                    "offset": 425
                },
                "end": {
                    "column": 41,
                    "line": 12,
                    "offset": 434
                },
                "source": "supense组件"
            }
        },
        "patchFlag": "-1 /* HOISTED */",
        "isBlock": false,
        "disableTracking": false,
        "loc": {
            "start": {
                "column": 13,
                "line": 12,
                "offset": 406
            },
            "end": {
                "column": 47,
                "line": 12,
                "offset": 440
            },
            "source": "<div id=\"suspense\">supense组件</div>"
        }
    },
    {
        "type": 13,
        "tag": "\"div\"",
        "children": {
            "type": 2,
            "content": "telport组件",
            "loc": {
                "start": {
                    "column": 18,
                    "line": 15,
                    "offset": 517
                },
                "end": {
                    "column": 27,
                    "line": 15,
                    "offset": 526
                },
                "source": "telport组件"
            }
        },
        "patchFlag": "-1 /* HOISTED */",
        "isBlock": false,
        "disableTracking": false,
        "loc": {
            "start": {
                "column": 13,
                "line": 15,
                "offset": 512
            },
            "end": {
                "column": 33,
                "line": 15,
                "offset": 532
            },
            "source": "<div>telport组件</div>"
        }
    }
]

具体实现:

function genHoists(hoists: (JSChildNode | null)[], context: CodegenContext) {
  if (!hoists.length) {
    return
  }
  context.pure = true
  const { push, newline, helper, scopeId, mode } = context
  const genScopeId = !__BROWSER__ && scopeId != null && mode !== 'function'
  newline() // 添加新行

  // push scope Id before initializing hoisted vnodes so that these vnodes
  // get the proper scopeId as well.
  if (genScopeId) {
    push(`${helper(PUSH_SCOPE_ID)}("${scopeId}")`)
    newline()
  }

  hoists.forEach((exp, i) => {
    if (exp) {
      push(`const _hoisted_${i + 1} = `)
      genNode(exp, context)
      newline()
    }
  })

  if (genScopeId) {
    push(`${helper(POP_SCOPE_ID)}()`)
    newline()
  }
  context.pure = false
}