我们上一章分析了html解析开始标签的处理,我们本章来看一下结束标签的处理。
以下就是匹配结束标签代码:
// End tag:
const endTagMatch = html.match(endTag) // 匹配 </
if (endTagMatch) {
// 开始位置
const curIndex = index
// 减去对应的长度
advance(endTagMatch[0].length)
// 解析结束标签
parseEndTag(endTagMatch[1], curIndex, index)
continue
}
我们先简单说一下 parseEndTag 的分析,为了减少篇幅我们就只帖出关键代码了
1、匹配 “stack” 数组里面相同标签名称的对象
// 转换为小写
lowerCasedTagName = tagName.toLowerCase()
// 匹配相同的tag标签
for (pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos].lowerCasedTag === lowerCasedTagName) {
break
}
}
2、调用 “end” 函数
if (options.end) {
options.end(stack[i].tag, start, end)
}
那么我们来看一下 “end” 函数的代码:
end (tag, start, end) {
// 取出 stack 数组的最后一项
const element = stack[stack.length - 1]
// 删除数组的最后一项
stack.length -= 1
// 当前标签的附件标签
currentParent = stack[stack.length - 1]
if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) {
// 标签结束位置
element.end = end
}
closeElement(element)
}
可看到其实主要的工作是在closeElement 函数,我们简单总结一下该函数的功能,先看代码:
function closeElement (element) {
trimEndingWhitespace(element)
c
// tree management
if (!stack.length && element !== root) {
// allow root elements with v-if, v-else-if and v-else
if (root.if && (element.elseif || element.else)) {
if (process.env.NODE_ENV !== 'production') {
checkRootConstraints(element)
}
addIfCondition(root, {
exp: element.elseif,
block: element
})
} else if (process.env.NODE_ENV !== 'production') {
warnOnce(
`Component template should contain exactly one root element. ` +
`If you are using v-if on multiple elements, ` +
`use v-else-if to chain them instead.`,
{ start: element.start }
)
}
}
if (currentParent && !element.forbidden) {
if (element.elseif || element.else) {
processIfConditions(element, currentParent)
} else {
if (element.slotScope) {
// scoped slot
// keep it in the children list so that v-else(-if) conditions can
// find it as the prev node.
const name = element.slotTarget || '"default"'
;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[name] = element
}
currentParent.children.push(element)
element.parent = currentParent
}
}
// final children cleanup
// filter out scoped slots
element.children = element.children.filter(c => !(c: any).slotScope)
// remove trailing whitespace node again
trimEndingWhitespace(element)
// check pre state
if (element.pre) {
inVPre = false
}
if (platformIsPreTag(element.tag)) {
inPre = false
}
// apply post-transforms
for (let i = 0; i < postTransforms.length; i++) {
postTransforms[i](element, options)
}
}
接下来我们拆分一下该函数。
1、移除子节点是空白节点的标签(node.type === 3 && node.text === '')
代码如下:
trimEndingWhitespace(element)
2、处理该节点的属性以及指令
代码如下:
if (!inVPre && !element.processed) {
element = processElement(element, options)
}
processElement 函数有如下处理:
1、处理v-bind指令
2、处理 ref 属性
3、处理 slot 插槽一个 slot 标签
4、处理component 组件
5、处理标签属性(attr)
6、是否存在 style和 :style冲突
7、是否存在 class和 :class冲突
class
我们来看看以上代码的作用:
允许根元素具有v-if、v-else-if和v-else。
checkRootConstraints(element)
checkRootConstraints 函数,我们已经看过很多遍了,验证不能是 “slot” 和 “template” 标签,并且不是存在 v-for 指令,这个大家都知道,只能有一个根元素。
if (element.elseif || element.else) {
processIfConditions(element, currentParent)
}
如果 “elseif”指令存在或者“else” 指令存在,就需要 验证是否存在 "v-if" 指令是存在存在,因为他们是对应的。
element.children = element.children.filter(c => !(c: any).slotScope)
处理子元素的作用域。
if (platformIsPreTag(element.tag)) {
inPre = false
}
是否为平台的 pre 标签。
for (let i = 0; i < postTransforms.length; i++) {
postTransforms[i](element, options)
}
最后调用 postTransforms 函数,如果存在的话,
这个我也没搞明白怎么用的,如果有人知道欢迎指点迷津啊,感激不尽。