1
头图

The most comfortable way to write petite-vue and Vue is to bind events through @click and the framework will automatically unbind them for us when elements are removed. Eliminate the hassle of going through jQuery in the past. And event binding is a directive in petite-vue, similar to other directives.

In-depth v-on how it works

walk methods when parsing the template will traverse the characteristic elements of the collection el.attributes , when the attribute name name match v-on or @ , then push the attribute name and attribute value into the tail of the deferred queue, when all the attributes of the current element are bound and v-modal processed and all the attributes of the child elements are bound, v-modal and event binding processing before processing.

The question is, why should event binding be processed at the end?

 //文件 ./src/on.ts
const systemModifiers = ['ctrl', 'shift', 'alt', 'meta']

const modifiersGuards: Record<
  string,
  (e: Event, modifiers: Record<string, true>) => void | boolean
> = {
  stop: e => e.stopPropagation(),
  prevent: e => e.preventDefault(),
  self: e => e.target !== e.currentTarget,
  ctrl: e => !(e as KeyedEvent).ctrlKey,
  shift: e => !(e as KeyedEvent).shiftKey,
  alt: e => !(e as KeyedEvent).altKey,
  meta: e => !(e as KeyedEvent).metaKey,
  left: e => 'button' in e && (e as MouseEvent).button !== 0,
  middle: e => 'button' in e && (e as MouseEvent).button !== 1,
  right: e => 'button' in e && (e as MouseEvent).button !== 2,
  /* @click.alt.shift 将分别匹配alt和shift两个modifiers guards,当此时按alt+shift+ctrl时,两个modifiers guards均通过。
   * 而@click.alt.shift.exact 将分别匹配alt、shift和exact,当此时按alt+shift+ctrl时,前面两个modifiers guards均通过,但最后的exact guard将返回true,不执行事件回调函数。
   */
  exact: (e, modifiers) => 
    systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers[m])
}

export const on: Directive({ el, get, exp, arg, modifiers }) => {
  let handler = simplePathRE.test(exp)
    ? get(`(e => ${exp}(e)`)
    : get(`($event => { ${exp} })`)

  if (arg === 'vue:mounted') {
    // 假如绑定的是生命周期函数mounted,但由于当前元素早已添加到DOM树上,因此将函数压入micro queue执行
    nextTick(handler)
    return
  }
  else if (arg === 'vue:unmounted') {
    // 假如绑定的是生命周期函数unmounted,则返回cleanup函数
    return () => handler()
  }

  if (modifiers) {
    // 如果存在modifiers,则对事件绑定进行增强

    if (arg === 'click') {
      // @click.right 对应的DOM事件是contextmenu
      if (modifiers.right) arg = 'contextmenu'
      // @click.middle 对应的DOM事件是mouseup
      if (modifiers.middle) arg = 'mouseup'
    }

    const raw = hanlder
    handler = (e: Event) => {
      if ('key' in e && !(hyphenate((e as KeyboardEvent).key) in modifiers)) {
        /* 如果为键盘事件,键不在没有在modifiers中指定则不执行事件回调函数
         * key值为a、b、CapsLock等,hyphenate将CapsLock转换为caps-lock
         */ 
        return
      }
      for (const key in modifiers) {
        // 执行modifiers对应的逻辑,若返回true则不执行事件回调函数
        const guard = modiferGuards[key]
        if (guard && guard(e, modifiers)) {
          return
        }
        return raw(e)
      }
    }
  }

  // 居然没有返回cleanup函数??大家可以去贡献代码了哈哈
  listen(el, arg, handler, modifers)
}
 //文件 ./src/utils.ts

export const listen = (
  el: Element,
  event: string,
  handler: any,
  opotions?: any
) => {
  el.addEventListener(event, handler, options)
}

Summarize

Now that we have understood the working principle of v-bind and v-on , let's take a look at v-modal later!

"Anatomy of Petite-Vue Source Code" booklet

"Petite-Vue Source Code Analysis" combines examples to interpret the source code line by line from online rendering, responsive system and sandbox model, and also makes a detailed analysis of the SMI optimization dependency cleaning algorithm using the JS engine in the responsive system. It is definitely an excellent stepping stone before getting started with Vue3 source code. If you like it, remember to forward it and appreciate it!


肥仔John
2.8k 声望1.8k 粉丝

《Petite-Vue源码剖析》作者