perf: template abbreviation of end tag
CathLee opened this issue · 3 comments
related #112
I've been working on optimizing the abbreviation of end tags in templates and would greatly appreciate it if you could review my solution at your convenience.So there is my solution:
Take the HTML code <div><span></span></div> as an example. The parsing mechanism sequentially executes handling (<div, <span) and closing (</) actions. My optimization's primary objective is to leverage self-closing tags whenever feasible, especially for the sequence's terminal element.
My strategy unfolds in two steps:
- Determine whether a
closeaction directly follows ahandleaction, identified bythis.currentStage === 'stateInTagName'. When this condition is met, we flag the current Node withisShouldSelfClosing. This flag is later used during thetransformElementphase to implement the optimization. If the condition isn't met, we don't set the flag. - Modify the
currentStageattribute to accurately represent the ongoing parsing phase, thus accommodating various tag processing scenarios.
here is some draft code
// packages/compiler-core/src/tokenizer.ts
private stateInClosingTagName(c: number): void {
if (c === CharCodes.Gt || isWhitespace(c)) {
this.cbs.onclosetag(this.sectionStart, this.index)
this.cbs.onclosetag(
this.sectionStart,
this.index,
this.currentStage === 'stateInTagName',// `close` operation after `handle` operation
)
this.sectionStart = -1
this.state = State.AfterClosingTagName
this.stateAfterClosingTagName(c)
}
this.currentStage === 'stateInClosingTagName' //change currentStage in different flow
}// packages/compiler-core/src/parser.ts
onclosetag(start, end, isLastElement) {
const name = getSlice(start, end)
if (!currentOptions.isVoidTag(name)) {
let found = false
for (let i = 0; i < stack.length; i++) {
const e = stack[i]
if (e.tag.toLowerCase() === name.toLowerCase()) {
found = true
// if is the isLastElement make a tag with `isShouldSelfClosing`
if (isLastElement) {
e.isShouldSelfClosing = true
}
if (i > 0) {
emitError(ErrorCodes.X_MISSING_END_TAG, stack[0].loc.start.offset)
}
for (let j = 0; j <= i; j++) {
const el = stack.shift()!
onCloseTag(el, end, j < i)
}
break
}
}
if (!found) {
emitError(ErrorCodes.X_INVALID_END_TAG, backTrack(start, CharCodes.Lt))
}
}
},// packages/compiler-vapor/src/transforms/transformElement.ts
const { node } = context
if (node.isShouldSelfClosing) {
context.template += context.childrenTemplate.join('')
} else {
context.template += `>` + context.childrenTemplate.join('')
}
context.template += `>` + context.childrenTemplate.join('')
// TODO remove unnecessary close tag, e.g. if it's the last element of the template
if (!isVoidTag(tag)) {
const { node } = context
if (node.isShouldSelfClosing) {
context.template += ` />`
} else {
context.template += `</${tag}>`
}
}I've integrated the isShouldSelfClosing flag into the standard Node structure and have made the necessary updates in tokenizer.ts. However, I'm uncertain if this is the best approach.
Any feedback or advice you could offer on this strategy would be incredibly valuable to me~~
ps:code is in https://github.com/CathLee/core-vapor/tree/transform/close_tag
Could you please create a PR for your code, so that we can discuss some details conveniently?
I added some unit tests for your reference.
https://github.com/vuejs/core-vapor/blob/a68445bdac84b4c49ca926378bfe77f8a9e73fbd/packages/compiler-vapor/__tests__/abbreviation.spec.ts
I added some unit tests for your reference. https://github.com/vuejs/core-vapor/blob/a68445bdac84b4c49ca926378bfe77f8a9e73fbd/packages/compiler-vapor/__tests__/abbreviation.spec.ts
sure!!🤓,i will try my best to do it right now🏎