vuejs / core-vapor

Vue Vapor is a variant of Vue that offers rendering without the Virtual DOM.
https://vapor-repl.netlify.app
MIT License
1.81k stars 86 forks source link

perf: template abbreviation of end tag #166

Open CathLee opened 5 months ago

CathLee commented 5 months ago

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:

  1. Determine whether a close action directly follows a handle action, identified by this.currentStage === 'stateInTagName'. When this condition is met, we flag the current Node with isShouldSelfClosing. This flag is later used during the transformElement phase to implement the optimization. If the condition isn't met, we don't set the flag.
  2. Modify the currentStage attribute 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

sxzz commented 5 months ago

Could you please create a PR for your code, so that we can discuss some details conveniently?

sxzz commented 5 months ago

I added some unit tests for your reference. https://github.com/vuejs/core-vapor/blob/a68445bdac84b4c49ca926378bfe77f8a9e73fbd/packages/compiler-vapor/__tests__/abbreviation.spec.ts

CathLee commented 4 months ago

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🏎