vue3事件缓存(cacheHandlers)

<div @click="clickEvent">Hello World!</div>

vue2编译成render函数

(_ctx, _cache) => {
  return (_openBlock(), _createElementBlock("div", { onClick: _ctx.clickEvent }, "Hello World!", 8 /* PROPS */, ["onClick"]))
}

// Check the console for the AST

vue3经过编程render函数

import { openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", {
    onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.clickEvent && _ctx.clickEvent(...args)))
  }, "Hello World!"))
}

// Check the console for the AST

为什么要单独加缓存?

更新事件的似乎,如果需要卸载事件,太消耗性能,如果我们可以直接更改老的事件的内存地址,何乐而不为

export function patchEvent(
  el: Element & { _vei?: Record<string, Invoker | undefined> },
  rawName: string,
  prevValue: EventValue | null,
  nextValue: EventValue | null,
  instance: ComponentInternalInstance | null = null
) {
  // vei = vue event invokers
  const invokers = el._vei || (el._vei = {})
  const existingInvoker = invokers[rawName]
  if (nextValue && existingInvoker) {
    // patch
    existingInvoker.value = nextValue
  } else {
    const [name, options] = parseName(rawName)
    if (nextValue) {
      // add
      const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
      addEventListener(el, name, invoker, options)
    } else if (existingInvoker) {
      // remove
      removeEventListener(el, name, existingInvoker, options)
      invokers[rawName] = undefined
    }
  }
}

上面代码做的事情

  1. 给当前的el上挂载一个_vei属性(vue event invokers)去缓存当前这条元素身上的事件
  2. 进行diff比对的时候,如果有老的同名的事件就直接替换老的事件的内存地址(这样就少了一次移除事件+新增事件的性能消耗)
  3. 如果没有就算是新增的事件,并且如果新增的事件不存在,才真正去移除老的事件,如果新增的事件在,并且没有老的事件绑定,则重新创建缓存并且绑定到el上

invoker里面的结构类似

{
    click:[event....]
    change:[event...]
}
Last modification:July 18, 2022
If you think my article is useful to you, please feel free to appreciate