在一次crud的时候,碰到了这样一个接口返回的数据是这样子的
<MachineModel machModelId="915432156" machModelName="故障标准传动链" machLayoutStatus="0">
<item machModelId="915432156" itemtype="7" id="915432156_1" name="行星" bearingType="SRB/CARB"
bearingCompany="FAG" ftf="0.455" bsf="10.718" bpfo="13.64" bpfi="16.36" position_x="127"
position_y="150" speedRate="0.008350566967190259" left="8" right="16">
</item>
<item machModelId="915432156" itemtype="7" id="915432156_2" name="恒星" bearingType="F-573602.NCF-WPOS"
bearingCompany="FAG" ftf="0.476" bsf="20.7" bpfo="30.9" bpfi="34.1" position_x="287"
position_y="150" speedRate="0.008350566967190259" left="16" right="3">
</item>
</MachineModel>
后端将xml配置文件读出来返回给我了,要我拿到标签上的数据在页面上的位置做展示。嗯?
暂时没什么好的想法, 那么就直接innerHtml
然后document.getElementsByTagName
吧。
写完了之后,发现可维护性太差了吧。 写的确实有些辣眼睛,想到是不是可以把vue的模板解析的代码借鉴(抄一抄)下,解析模板字符串,拿到标签上的数据。
正则
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; // aaa-bbb // 命名空间
const qnameCapture = `((?:${ncname}\\:)?${ncname})`; // <aaa:bbb> ?: 代表匹配不捕获
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的 </div>
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配属性的
const startTagClose = /^\s*(\/?)>/; // 匹配标签结束的 > 也能是自闭合标签,所以 />
创建parseHtml函数,通过闭包的形式创建辅助函数公用html模板字符串参数。循环html字符串,处理不同的情况,处理之后进行截取。
while (html) {
let textEnd = html.indexOf('<')
// 判断是否是< 开头
if (textEnd === 0) {
// 如果进来了,那么这可能是开始标签也可能是结束标签
let startTagMatch = parseStartTag(html) // 对开始标签进行解析 <div a=1 b=2>, 返回处理后的结果,主要数据为tagName
if (startTagMatch) {
// 进栈
start(startTagMatch.tagName)
// 如果匹配到了自闭和标签
if (startTagMatch.autoEnd) {
end(startTags[startTags.length - 1])
}
continue // 如果开始标签匹配完毕后,继续下一次匹配
}
// 尝试匹配结束标签
let endTagMatch = html.match(endTag)
if (endTagMatch) {
advance(endTagMatch[0].length)
end(endTagMatch[1])
continue
}
}
let text
// 如果大于0,则说明距离下一个开始标签还有距离, 而且中间值是文本<div>111<span>222</span></div>
if (textEnd >= 0) {
text = html.substring(0, textEnd)
advance(text.length)
// 这里去除字符串即可,无需进行额外处理
}
}
最核心的函数解析开始标签,拿到需要的属性
`function parseStartTag() {
let start = html.match(startTagOpen)
if (start) {
const match = {
tagName: start1, // 匹配到第一个分组, 在这里就是这个标签名。
attrs: []
}
advance(start[0].length) // start[0] 代表匹配到的结果
let endT, attr;
// 如果没有匹配到结束标签并且还能匹配到属性的话那么就开始解析属性
while (!(endT = html.match(startTagClose)) && (attr = html.match(attribute))) {
advance(attr[0].length) // 将属性去掉
match.attrs.push({
name: attr1,
value: attr3 || attr4 || attr[5]
})
}
stack.push({ tagName: match.tagName, attrs: match.attrs })
// 这里处理了自闭和标签
if (endT[0].trim() === ‘/>’ && html.trim().match(startTagClose)) {
let endTagMatch = html.match(startTagClose)
if (endTagMatch) {
advance(endTagMatch[0].length)
}
return { …match, autoEnd: true }
} else {
advance(endT[0].length) // 将开始标签的> 去掉
return match
}
}
}
`
匹配到开始标签,需要进栈
function start(tagName) {
startTags.push(tagName)
}
匹配到结束标签,需要进栈, 开始与结束标签进栈的目的是对匹配到的结束标签是否是上一次匹配到的开始标签,否则就报错。至于自闭合标签在parseStartTag函数中处理过了。
function end(tagName) {
endTags.push(tagName)
if (tagName === startTags[startTags.length - 1]) {
startTags.splice(-1)
} else {
console.error('开始标签与结束标签不匹配')
// 进行异常处理
}
}
对html字符串进行截取
function advance(n) {
html = html.substring(n)
}
完整代码
返回的处理结果
谢谢看完
如果这篇文章对您有所帮助的话,那就点个赞趴。
往期文章:
通俗易懂的实现符合promise A+ 规范的自定义promise
js异步编程,eventLoop、消息队列都是做什么的? 什么是宏任务,什么是微任务
作者:不如吃茶去
链接:https://juejin.im/post/6894159094811688967
看完两件小事
如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:
- 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
- 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程
本文著作权归作者所有,如若转载,请注明出处
转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com