const TextModes = { DATA: "DATA", RCDATA: "RCDATA", RAWTXT: "RAWTXT", CDATA: "CDATA",}//优化的解析函数export function parser1(str) { const context = { source: str, mode: TextModes.DATA, //advanceBy函数用来消费指定数量的字符,它接收一个数字作为参数 advanceBy(num) { context.source = context.source.slice(num) }, //消费空白字符串 advanceSpaces() { const match = /^[\t\r\n\f ]+/.exec(context.source) if (match) { context.advanceBy(match[0].length) } }, } const nodes = parseChildren(context, []) return { type: "Root", children: nodes, }}function isEnd(context, ancestors) { if (!context.source) return true for (let i = ancestors.length - 1; i >= 0; i--) { if (context.source.startsWith(`</${ancestors[i].tag}`)) return true }}//解析文本插值{{}}function parseInterpolation(context) { context.advanceBy("{{".length) let closeIndex = context.source.indexOf("}}") if (closeIndex < 0) { console.error("插值缺少结束界定符") } const content = context.source.slice(0, closeIndex) context.advanceBy(content.length) context.advanceBy("}}".length) return { type: "Expression", content, }}//解析文本function parseText(context) { //文本结尾索引 let endIndex = context.source.length //<位置 let ltIndex = context.source.indexOf("<") //{{位置 const delimiterIndex = context.source.indexOf("{{") if (ltIndex > -1 && ltIndex < endIndex) { endIndex = ltIndex } if (delimiterIndex > -1 && delimiterIndex < endIndex) { endIndex = delimiterIndex } const content = context.source.slice(0, endIndex) context.advanceBy(content.length) return { type: "Text", content, }}//解析属性function parseAttributes(context) { const props = [] const { advanceSpaces, advanceBy } = context while (!context.source.startsWith(">") && !context.source.startsWith("/>")) { const match = /^[^\t\r\n\f />][^\t\r\n\f />=]*/.exec(context.source) const name = match[0] advanceBy(name.length) advanceSpaces() //消费等于号 advanceBy(1) advanceSpaces() let value = "" const quote = context.source[0] const isQuoted = quote === "'" || quote === '"' if (isQuoted) { advanceBy(1) const endQuoteIndex = context.source.indexOf(quote) if (endQuoteIndex > -1) { value = context.source.slice(0, endQuoteIndex) advanceBy(value.length) advanceBy(1) } else { console.error("缺少引号") } } else { const match = /^[^\t\r\n\f />]+/.exec(context.source) value = match[0] advanceBy(value.length) } advanceSpaces() props.push({ type: "Attribute", name, value, }) } return props}//解析标签function parseTag(context, type = "start") { const { advanceBy, advanceSpaces } = context const match = type === "start" ? /^<([a-z][^\t\r\n\f />]*)/i.exec(context.source) : /^<\/([a-z][^\t\r\n\f />]*)/i.exec(context.source) const tag = match[1] advanceBy(match[0].length) advanceSpaces() const props = parseAttributes(context) const isSelfClosing = context.source.startsWith("/>") advanceBy(isSelfClosing ? 2 : 1) return { type: "Element", tag, props, children: [], isSelfClosing, }}//解析元素function parseElement(context, ancestors) { const element = parseTag(context) if (element.isSelfClosing) return element if (element.tag === "textarea" || element.tag === "title") { context.mode = TextModes.RCDATA } else if (/style|xmp|iframe|noembed|noframes|noscript/.test(element.tag)) { context.mode = TextModes.RAWTXT } else { context.mode = TextModes.DATA } ancestors.push(element) element.children = parseChildren(context, ancestors) ancestors.pop() if (context.source.startsWith(`</${element.tag}`)) { parseTag(context, "end") } else { console.error(`${element.tag}标签缺少闭合标签`) } return element}function parseChildren(context, ancestors) { let nodes = [] const { mode, source } = context while (!isEnd(context, ancestors)) { let node if (mode === TextModes.DATA || mode === TextModes.RCDATA) { if (mode === TextModes.DATA && source[0] === "<") { if (source[1] === "!") { if (source.startsWith("<!--")) { //注释 node = parseComment(context) } else if (source.startsWith("<![CDATA[")) { //CDATA node = parseCDATA(context, ancestors) } } else if (source[1] === "/") { //结束标签 console.error("无效的结束标签") continue } else if (/[a-z]/i.test(source[1])) { //标签 node = parseElement(context, ancestors) } } else if (source.startsWith("{{")) { //插值 node = parseInterpolation(context) } } if (!node) { //node不存在说明是解析文本 node = parseText(context) } nodes.push(node) } return nodes}// const tree = parser1("<div id='foo' v-show='display'></div>")const tree = parser1("<div>{{vue}}</div>")console.log(tree)