携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
HTMLParserOptions.end()
在解析到标签结束部分时被调用,这部分代码主要用来处理之前的 stack 元素栈
function end(tag, start, end) {
const element = stack[stack.length - 1]
stack.length -= 1
currentParent = stack[stack.length - 1]
if (__DEV__ && options.outputSourceRange) {
element.end = end
}
closeElement(element)
},
复制代码
这部分逻辑很简单:
- 首先是取出当前元素保存到 element (ast)对象中,用来后面调用 closeElement() 方法时校验该元素解析结果并闭合该标签
- 然后将 stack 栈的长度减少 1,并重新设置当前解析元素的父元素 currentParent 为上一个栈中元素
- 如果是开发环境并且开启了源代码位置定位的,还会把结束位置插入到当前元素的 ast element 对象中
HTMLParserOptions.chars()
在解析到文本输出时被调用,这部分代码主要用来处理文本出现的 异常位置警告 、处理文本字符串 和 生成文本 ast 对象
chars(text: string, start?: number, end?: number) {
// 第一部分:判断
if (!currentParent) {
if (__DEV__) {
if (text === template) warnOnce('')
if ((text = text.trim())) warnOnce('')
}
return
}
if (isIE && currentParent.tag === 'textarea' && currentParent.attrsMap.placeholder === text) {
return
}
// 第二部分:处理
const children = currentParent.children
if (inPre || text.trim()) {
text = isTextTag(currentParent) ? text : (decodeHTMLCached(text) as string)
} else if (!children.length) {
text = ''
} else if (whitespaceOption) {
if (whitespaceOption === 'condense') {
text = lineBreakRE.test(text) ? '' : ' '
} else {
text = ' '
}
} else {
text = preserveWhitespace ? ' ' : ''
}
// 第三部分:生成 ast
if (text) {
if (!inPre && whitespaceOption === 'condense') {
text = text.replace(whitespaceRE, ' ')
}
let res
let child: ASTNode | undefined
if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
child = {
type: 2,
expression: res.expression,
tokens: res.tokens,
text
}
} else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
child = { type: 3, text }
}
if (child) {
if (__DEV__ && options.outputSourceRange) {
child.start = start
child.end = end
}
children.push(child)
}
}
}
复制代码
1. 异常位置警告(校验文本位置 )
这里有两种判断:
-
当 没有父节点 的时候(即此时的文本在组件的最外层位置),如果直接出现了文本,则退出函数并报错
如果一个组件下 只有文本节点 (即 解析的text === template 模板),则警告组件下必须有根节点,不能只是文本
如果还有其他节点(即取消尾部空格后不是空字符串),则警告该部分字符串会被忽略无法渲染
-
修复 ie 浏览器下textare 标签的 placeholder 异常
2. 处理文本字符串
这里首先会获取父节点的 子节点数组,也就是 currentParent.children,然后对该文本部分进行处理(按照标签格式、子节点数量、空格删除配置等方式进行转换)。
3. 生成文本 ast 对象
这里首先会压缩原文本的空格符合换行符,将连续的这些字符转换成一个空格符。之后进行判断并处理 ast 对象
当前文本节点 不在 v-pre 声明 的标签内部且 不是一个空格字符,并且解析到特殊标识(双大括号等),才创建文本 ast 对象
此时会循环匹配结果并生成条件字符串,以及对应的解析 token,此时节点 ast 的类型为2
这里会使用 parseText 方法来解析该文本,具体 parseText 的解析过程在后面讲
如果文本节点 不是一个空格字符,或者父节点长度为0,或者 上一个同级子节点 也不是一个空格字符,才创建文本 ast 对象
当经过上面的判断之后有文本节点 ast 对象的话,才将该对象插入到父节点的 children 数组中。
HTMLParserOptions.comment()
在解析到注释部分的时候调用,用来处理注释节点
function comment(text: string, start, end) {
if (currentParent) {
const child: ASTText = {
type: 3,
text,
isComment: true
}
if (__DEV__ && options.outputSourceRange) {
child.start = start
child.end = end
}
currentParent.children.push(child)
}
}
复制代码
这里进行一次判断,只有内部的注释标签内容才会生成 ast 对象,与根节点同级的注释将直接忽略。