<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> <script> // 构造parse函数 const State = { initial: 1, // 初始状态 tagOpen: 2, // 标签开始状态 tagName: 3, // 标签名称状态 text: 4, // 文本状态 tagEnd: 5, // 结束标签状态 tagEndName: 6 // 结束标签名称状态 } const isAlpha = (char) => { // 用于判断是否是字母 return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' } const tokenize = (str) => { let currentState = State.initial const chars = [] // 用于缓存字符 const tokens = [] // 存储生成的token,并作为函数返回值进行返回 while (str) { // 使用while循环开启自动机,只要str没有被消费尽,自动机就会一直运行 const char = str[0] switch (currentState) { case State.initial: if (char === '<') { currentState = State.tagOpen str = str.slice(1) } else if (isAlpha(char)) { currentState = State.text chars.push(char) str = str.slice(1) } break; case State.tagOpen: if (isAlpha(char)) { currentState = State.tagName chars.push(char) str = str.slice(1) } else if (char === '/') { currentState = State.tagEnd str = str.slice(1) } break; case State.tagName: if (isAlpha(char)) { chars.push(char) str = str.slice(1) } else if (char === '>') { currentState = State.initial tokens.push({ type: 'tag', name: chars.join('') }) chars.length = 0 str = str.slice(1) } break; case State.text: if (isAlpha(char)) { chars.push(char) str = str.slice(1) } else if (char === '<') { currentState = State.tagOpen tokens.push({ type: 'text', content: chars.join('') }) chars.length = 0 str = str.slice(1) } break; case State.tagEnd: if (isAlpha(char)) { currentState = State.tagEndName chars.push(char) str = str.slice(1) } break; case State.tagEndName: if (isAlpha(char)) { chars.push(char) str = str.slice(1) } else if (char === '>') { currentState = State.initial tokens.push({ type: 'tagEnd', name: chars.join('') }) chars.length = 0 str = str.slice(1) } break; } } return tokens } // console.log(tokenize('<div><p>vue</p><p>template</p></div>')); const parse = (str) => { const tokens = tokenize(str) const root = { type: 'Root', children: [] } const elementStack = [root] // 及其重要,构造父子关系的关键角色 while (tokens.length) { const parent = elementStack.at(-1) const t = tokens[0] switch (t.type) { case 'tag': const elementNode = { type: 'Element', tag: t.name, children: [] } parent.children.push(elementNode) elementStack.push(elementNode) break; case 'text': const textNode = { type: 'Text', content: t.content } parent.children.push(textNode) break; case 'tagEnd': elementStack.pop() break; } tokens.shift() } return root } console.log(parse('<div><p>vue</p><p>template</p></div>')); </script> </html>