下面是关于“vue parseHTML 函数源码解析AST基本形成”的完整攻略:
什么是parseHTML函数
parseHTML是Vue.js中的一个函数,主要用来将HTML字符串解析成AST对象。AST(Abstract Syntax Tree)是指抽象语法树,它是源代码的抽象语法结构的树状表现形式。Vue的模板就是由HTML模板和vue上下文中的数据组合而成。parseHTML的作用是将HTML模板转化为AST对象,方便后续编译和渲染。
parseHTML的源码解析
下面是parseHTML的基本实现过程:
function parseHTML(html, options) {
var stack = []
var expectHTML = options.expectHTML
var isUnaryTag$$1 = options.isUnaryTag || no
var canBeLeftOpenTag$$1 = options.canBeLeftOpenTag || no
var index = 0
var last, lastTag
while (html) {
last = html
// Make sure we're not in a plaintext content element like script/style
if (!lastTag || !isPlainTextElement(lastTag)) {
var textEnd = html.indexOf('<')
if (textEnd === 0) {
// Comment
if (comment.test(html)) {
var commentEnd = html.indexOf('-->')
if (commentEnd >= 0) {
advance(commentEnd + 3)
continue
}
}
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
if (conditionalComment.test(html)) {
var conditionalEnd = html.indexOf(']>')
if (conditionalEnd >= 0) {
advance(conditionalEnd + 2)
continue
}
}
// Doctype
var doctypeMatch = html.match(doctype)
if (doctypeMatch) {
advance(doctypeMatch[0].length)
continue
}
// End tag
var endTagMatch = html.match(endTag)
if (endTagMatch) {
var curIndex = index
advance(endTagMatch[0].length)
parseEndTag(endTagMatch[1], curIndex, index)
continue
}
// Start tag
var startTagMatch = parseStartTag()
if (startTagMatch) {
handleStartTag(startTagMatch)
if (shouldIgnoreFirstNewline(last, html)) {
advance(1)
}
continue
}
}
var text = void 0
if (textEnd >= 0) {
text = html.substring(0, textEnd)
advance(textEnd)
} else {
text = html
html = ''
}
if (options.chars && text) {
options.chars(text)
}
} else {
var stackedTag = lastTag.toLowerCase()
var reStackedTag = new RegExp('([\\s\\S]*?)(</' + stackedTag + '[^>]*>)', 'i')
var match = html.match(reStackedTag)
if (match) {
var text$$1 = match[1]
if (options.chars) {
options.chars(text$$1)
}
advance(text$$1.length + match[2].length)
parseEndTag(stackedTag, index - text$$1.length, index)
}
if (!canBeLeftOpenTag$$1(stackedTag)) {
parseEndTag(stackedTag)
}
}
if (html === last) {
options.chars && options.chars(html)
break
}
}
// Clean up any remaining tags
parseEndTag()
function advance(n) {
index += n
html = html.substring(n)
}
function parseStartTag() {
var start = html.match(startTagOpen)
if (start) {
var match = {
tagName: start[1],
attrs: [],
start: index
}
advance(start[0].length)
var end, attr
while (!(end = html.match(startTagClose)) && (attr = html.match(dynamicArgAttribute) || html.match(attribute))) {
attr.start = index
advance(attr[0].length)
attr.end = index
match.attrs.push(attr)
}
if (end) {
match.unarySlash = end[1]
advance(end[0].length)
match.end = index
return match
}
}
}
function handleStartTag(match) {
var tagName = match.tagName
var unarySlash = match.unarySlash
if (expectHTML) {
if (lastTag === 'p' && isNonPhrasingTag(tagName)) {
parseEndTag(lastTag)
}
if (canBeLeftOpenTag$$1(tagName) && lastTag === tagName) {
parseEndTag(tagName)
}
}
var unary = isUnaryTag$$1(tagName) || !!unarySlash
var l = match.attrs.length
var attrs = new Array(l)
for (var i = 0; i < l; i++) {
var args = match.attrs[i]
var value = args[3] || args[4] || args[5] || ''
var shouldDecodeNewlines = tagName === 'a' && args[1] === 'href'
? options.shouldDecodeNewlinesForHref
: options.shouldDecodeNewlines
attrs[i] = {
name: args[1],
value: decodeAttr(value, shouldDecodeNewlines)
}
if (process.env.NODE_ENV !== 'production' && options.outputSourceRange) {
attrs[i].start = args.start + args[0].match(/^\s*/).length
attrs[i].end = args.end
}
}
if (!unary) {
stack.push({ tag: tagName, lowerCasedTag: tagName.toLowerCase(), attrs: attrs })
lastTag = tagName
unarySlash = ''
}
if (options.start) {
options.start(tagName, attrs, unary, match.start, match.end)
}
}
function parseEndTag(tagName, start, end) {
var pos, lowerCasedTagName
if (start == null) {
start = index
}
if (end == null) {
end = index
}
if (tagName) {
lowerCasedTagName = tagName.toLowerCase()
}
// Find the closest opened tag of the same type
if (tagName) {
for (pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos].lowerCasedTag === lowerCasedTagName) {
break
}
}
} else {
// If no tag name is provided, clean shop
pos = 0
}
if (pos >= 0) {
// Close all the OPENED tags that appear before the CLOSE tag
for (var i = stack.length - 1; i >= pos; i--) {
if (process.env.NODE_ENV !== 'production' && (i > pos || !tagName) && options.warn) {
options.warn(
("tag <" + (stack[i].tag) + "> has no matching end tag.")
)
}
if (options.end) {
options.end(stack[i].tag, start, end)
}
}
// Remove the closed tags from the stack
stack.length = pos
lastTag = pos && stack[pos - 1].tag
} else if (lowerCasedTagName === 'br') {
if (options.start) {
options.start(tagName, [], true, start, end)
}
} else if (lowerCasedTagName === 'p') {
if (options.start) {
options.start(tagName, [], false, start, end)
}
if (options.end) {
options.end(tagName, start, end)
}
}
}
}
实现逻辑解析
上述代码实现了parseHTML函数,其实现逻辑主要有以下几个步骤:
- 首先定义了一个stack数组,用于存储解析中遍历到的元素信息。
- 循环处理html字符串,如果当前的元素不是纯文本节点,则查找接下来的标签,并依次处理。
- 如果发现标签是注释节点,则跳过。
- 如果发现标签是条件注释,则跳过。
- 如果发现标签是文档类型声明,则跳过。
- 如果发现标签是结束标签,则使用parseEndTag函数处理。
- 如果发现标签是开始标签,则使用parseStartTag函数处理。
- 处理结束后,再次使用parseEndTag函数处理stack数组中剩余的元素。
总体上,parseStartTag函数负责解析开始标签,handleStartTag函数负责处理解析到的开始标签;parseEndTag函数负责解析结束标签。在这三个函数的协调下,parseHTML函数完成了将HTML解析为AST对象的过程。
示例说明
下面给出两个使用parseHTML的示例,以更清晰地展示这个函数的用法:
示例1
<div>
<p>这是一段HTML模板</p>
<span>{{message}}</span>
</div>
import { parseHTML } from 'vue/src/compiler/parser/html-parser'
const html = '<div><p>这是一段HTML模板</p><span>{{message}}</span></div>'
const ast = parseHTML(html,{
expectHTML: true,
isUnaryTag: function () {},
canBeLeftOpenTag: function () {},
chars: function () {}
})
console.log(ast)
上述代码中,我们定义了一个HTML字符串,然后通过parseHTML函数将其解析为AST对象,并输出到控制台。其中,parseHTML函数的参数中包含一些配置项,包括是否期望解析为HTML,是否将指定标签解析为一元标签等。
示例2
<template>
<div>
<p>这是一段HTML模板</p>
<span>{{message}}</span>
</div>
</template>
import { parseHTML } from 'vue/src/compiler/parser/html-parser'
const html = '<div><p>这是一段HTML模板</p><span>{{message}}</span></div>'
const ast = parseHTML(html,{
expectHTML: true,
isUnaryTag: function (tag) {
return tag === 'template'
},
canBeLeftOpenTag: function () {},
chars: function () {}
})
console.log(ast)
上述代码中,我们定义了一个HTML字符串,其中包含了一个template标签。在解析时,我们通过将isUnaryTag函数的返回值设为true,将template标签解析为一元标签,而不是普通标签。
通过以上两个示例,我们可以看到,parseHTML函数具有很强的灵活性,可以通过不同的配置项对不同的HTML模板进行不同的解析处理。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:vue parseHTML 函数源码解析AST基本形成 - Python技术站