html子字符串,一种前端解析html字符串片段的方案求教

只测试或你给的这个字符串,其他的没测试过const template1 = `

1111

::content1

22222

333

::content2

content2::

333333

::content2

sdfsadfda

::content3

666

777

::content4

content4::

content3::

content2::

content1::

::content1

::content2

content2::

::content2

content2::

content1::`

type HeadingElementTagName = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'

type TreeNode = {

tagName: HeadingElementTagName

content: string

children: TreeNode[]

textContent: string

}

class Parser {

private index = 0

private REBlank = /\s/

private template: string = ''

private RELiteral = /[a-zA-Z0-9_]/

private skipBlank() {

while (

this.index < this.template.length &&

this.REBlank.test(this.template[this.index])

)

++this.index

}

private parents: TreeNode[] = []

private throwError() {

throw new Error(

`Unexpected token ${this.template[this.index]} at index ${this.index}`

)

}

private consume(chars: string): void {

if (!this.template.startsWith(chars, this.index)) this.throwError()

this.index += chars.length

}

private readTagName(): string {

let tagName = ''

while (

this.index < this.template.length &&

this.template[this.index] !== '>' &&

this.template[this.index] !== '/'

) {

tagName += this.template[this.index]

++this.index

}

if (!tagName) this.throwError()

return tagName

}

private readHeadingElement(): TreeNode {

//读取开始标签比如,

this.consume('

let tagNameStart = this.readTagName()

this.consume('>')

let textContent = ''

while (

this.index < this.template.length &&

this.template[this.index] !== '

) {

textContent += this.template[this.index]

++this.index

}

this.skipBlank()

//读取结束标签比如

this.consume('')

const indexEnd = this.index

const tagNameEnd = this.readTagName()

this.consume('>')

if (tagNameStart !== tagNameEnd)

throw new Error(

`mismatching tag expect ${tagNameStart} but got ${tagNameEnd} at index ${indexEnd}`

)

const headingNode: TreeNode = {

tagName: tagNameStart as HeadingElementTagName,

children: [],

content: '',

textContent,

}

return headingNode

}

readStartContent(): string {

this.consume('::')

let content = ''

while (

this.index < this.template.length &&

this.RELiteral.test(this.template[this.index])

) {

content += this.template[this.index]

++this.index

}

if (!content) this.throwError()

return content

}

readEndContent(): string {

let content = ''

while (

this.index < this.template.length &&

this.RELiteral.test(this.template[this.index])

) {

content += this.template[this.index]

++this.index

}

if (!content) this.throwError()

this.consume('::')

return content

}

/**

* 读取一个TreeNode节点

* @returns 读取的TreeNode节点

*/

generateTree(): TreeNode | null {

let resolvedRoot: TreeNode | null = null

const nodeContent: string[] = []

while (this.index < this.template.length) {

const headingNode = this.readHeadingElement()

const parentNode = this.parents[this.parents.length - 1] ?? null

if (!resolvedRoot) resolvedRoot = headingNode

if (parentNode) parentNode.children.push(headingNode)

this.parents.push(headingNode)

this.skipBlank()

//判断该节点是否有content有则读取,没有则返回

if (this.template.startsWith(':', this.index)) {

const content = (headingNode.content = this.readStartContent())

nodeContent.push(content)

this.skipBlank()

//判断该content下是否有子节点,有的话递归读取

if (this.template.startsWith('

this.generateTree()

}

this.skipBlank()

//没有到达contentEnd据徐读取子节点

if (

this.index >= this.template.length ||

!this.RELiteral.test(this.template[this.index])

)

continue

} else {

/**

* 该节点没有子节点直接返回 */

this.parents.pop()

this.skipBlank()

if (!nodeContent.length) break

}

//循环读取contentEnd

while (

nodeContent.length &&

this.index < this.template.length &&

this.RELiteral.test(this.template[this.index])

) {

const indexEnd = this.index

const contentEnd = this.readEndContent()

if (nodeContent[nodeContent.length - 1] !== contentEnd) {

throw new Error(

`Mismatching content expect ${headingNode.content} but got ${contentEnd} at ${indexEnd}`

)

}

nodeContent.pop()

this.parents.pop()

this.skipBlank()

}

//如果nodeContent为空说明该节点已经处理完毕,结束循环,继续父级的逻辑

if (!nodeContent.length) break

this.skipBlank()

}

return resolvedRoot

}

parse(str: string): null | TreeNode {

this.template = str

this.skipBlank()

if (this.index >= this.template.length) return null

const root: TreeNode = {

tagName: 'DummyRoot' as HeadingElementTagName,

content: '',

children: [],

textContent: '',

}

while (this.index < this.template.length) {

root.children.push(this.generateTree()!)

this.skipBlank()

}

return root

}

}

const parser = new Parser()

console.log(parser.parse(template1)?.children[0])

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值