实际使用
// 引入parse函数
import parse from './parse'
// 定义一个简单的模板字符
let templateStr = `
<div><h2 class='box' id='my-page'>你好</h2>
<ul>
<li><b>A</b></li>
<li>little</li>
<li>baby</li>
</ul>
</div>
`
// 实际调用
const ast = parse(templateStr)
console.log('ast', ast);
parse函数主函数
parse函数,实现输入一个模板结构 转换成包含dom信息的层级对象结构
// parse函数 主函数,实现输入一个模板结构 转换成包含dom信息的层级对象结构
export default function parse(templateStr) {
// 指针
let idx = 0
// 识别开始标签
let startRegExp = /^\<([a-z]+[1-6]?)(\s+[^\<]+)?\>/
// startRegExp.test(" <h2 class='box'>你好</h2>")
// 识别结束标签
let endRegExp = /^\<\/([a-z]+[1-6]?)\>/
// 识别文字部分
let wordRegExp = /([^\<]+)(\<[a-z]+[1-6]?\>|\<\/[a-z]+[1-6]?\>)/
// 剩余部分
let rest = ''
// 准备两个栈 存放dom标签和内容
let stack1 = []
let stack2 = [{ children: [] }]
while (idx < templateStr.length - 1) {
rest = templateStr.trim().substring(idx)
// 识别开始标签
if (startRegExp.test(rest)) {
let tag = rest.match(startRegExp)[1]
console.log('识别到开始标签 ', tag);
let attrStr = rest.match(startRegExp)[2]
let attrLength = attrStr != null ? attrStr.length : 0
// length+2 是因为要把<>加上 + 属性的字符长度
idx += tag.length + 2 + attrLength
// 进栈 stack1
stack1.push(tag)
attrStr ? stack2.push({
'tag': tag,
'children': [],
'attrs': parseAttr(attrStr)
}) : stack2.push({
'tag': tag,
'children': [],
})
// 识别结束标签
} else if (endRegExp.test(rest)) {
let tag = rest.match(endRegExp)[1]
console.log('识别到结束标签 ', tag);
// length+2 是因为要把</>加上
idx += tag.length + 3
let pop_tag = stack1.pop()
// 出栈 stack1
if (tag === pop_tag) {
let pop_arr = stack2.pop()
if (stack2.length > 0) {
stack2[stack2.length - 1]['children'].push(pop_arr)
}
} else {
throw new Error('标签没有封闭')
}
// 识别文字部分
} else if (wordRegExp.test(rest)) {
let word = rest.match(wordRegExp)[1]
if (word.trim()) {
// console.log('识别到文字部分 ', word);
// 入栈 stack2
stack2[stack2.length - 1].children.push({
text: word,
type: stack1.length - 1
})
}
idx += word.length
} else {
idx++
}
}
return stack2[0].children
}
parseAttr方法
把attrs: " class='box' id='my-page'"
转变为数组对象返回[{name: "class"value: "box"} ...]
function parseAttr(attrStr) {
let resultArr = []
attrStr.split(' ').map(item=>{
if (!!item) {
let arr = item.split('=')
resultArr.push({
name : arr[0],
// 取消双引号
value: arr[1].replace(/\"/g, '').replace(/\'/g, '')
})
}
})
return resultArr
}
总结:
- 要熟练使用正则表达式来匹配到标签与文字内容,以及test方法进行判断是否匹配,用match方法来获取到匹配到的部分;
- 使用到了栈的思想,用到了两个栈,存放dom标签和内容,以及会根据条件操作何时出栈与入栈;
- 指针思想,定义了一个idx的指针,更改每次进入循环的字符串的下标;