解析标签
element
的一个类型描述
export type ElementNode =
| PlainElementNode
| ComponentNode
| SlotOutletNode
| TemplateNode
export interface Node {
type: NodeTypes
loc: SourceLocation
}
export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT
ns: Namespace
tag: string
tagType: ElementTypes
isSelfClosing: boolean
props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[]
}
export interface PlainElementNode extends BaseElementNode {
tagType: ElementTypes.ELEMENT
codegenNode:
| VNodeCall
| SimpleExpressionNode // when hoisted
| CacheExpression // when cached by v-once
| undefined
ssrCodegenNode?: TemplateLiteral
}
export interface ComponentNode extends BaseElementNode {
tagType: ElementTypes.COMPONENT
codegenNode:
| VNodeCall
| CacheExpression // when cached by v-once
| undefined
ssrCodegenNode?: CallExpression
}
export interface SlotOutletNode extends BaseElementNode {
tagType: ElementTypes.SLOT
codegenNode:
| RenderSlotCall
| CacheExpression // when cached by v-once
| undefined
ssrCodegenNode?: CallExpression
}
export interface TemplateNode extends BaseElementNode {
tagType: ElementTypes.TEMPLATE
// TemplateNode is a container type that always gets compiled away
codegenNode: undefined
}
具体实现
function parseElement(
context: ParserContext,
ancestors: ElementNode[]
): ElementNode | undefined {
__TEST__ && assert(/^<[a-z]/i.test(context.source))
// Start tag.
const wasInPre = context.inPre
const wasInVPre = context.inVPre
const parent = last(ancestors)
// 解析标签
const element = parseTag(context, TagType.Start, parent)
const isPreBoundary = context.inPre && !wasInPre
const isVPreBoundary = context.inVPre && !wasInVPre
// 如果是单标签或空标签直接返回
if (element.isSelfClosing || context.options.isVoidTag(element.tag)) {
return element
}
// Children. 否则添加子元素
ancestors.push(element)
const mode = context.options.getTextMode(element, parent)
const children = parseChildren(context, mode, ancestors)
ancestors.pop()
// 2.x inline-template compat v2兼容处理
if (__COMPAT__) {
const inlineTemplateProp = element.props.find(
p => p.type === NodeTypes.ATTRIBUTE && p.name === 'inline-template'
) as AttributeNode
if (
inlineTemplateProp &&
checkCompatEnabled(
CompilerDeprecationTypes.COMPILER_INLINE_TEMPLATE,
context,
inlineTemplateProp.loc
)
) {
inlineTemplateProp.value!.content = getSelection(
context,
element.loc.end
).source
}
}
element.children = children
// End tag.
// 如果是结束标签
if (startsWithEndTagOpen(context.source, element.tag)) {
parseTag(context, TagType.End, parent)
} else {
emitError(context, ErrorCodes.X_MISSING_END_TAG, 0, element.loc.start)
if (context.source.length === 0 && element.tag.toLowerCase() === 'script') {
const first = children[0]
if (first && startsWith(first.loc.source, '<!--')) {
emitError(context, ErrorCodes.EOF_IN_SCRIPT_HTML_COMMENT_LIKE_TEXT)
}
}
}
element.loc = getSelection(context, element.loc.start)
if (isPreBoundary) {
context.inPre = false
}
if (isVPreBoundary) {
context.inVPre = false
}
return element
}