【Vue3 源码解析】v-model 和 emit

v-model 部分源码:

export const vModelText: ModelDirective<
  HTMLInputElement | HTMLTextAreaElement
> = {
  // el dom节点对象; binding 对象; vnode
  created(el, { modifiers: { lazy, trim, number } }, vnode) {
    // 获取props中的modelValue属性对应的函数
    el[assignKey] = getModelAssigner(vnode)
    // number修饰符
    const castToNumber =
      number || (vnode.props && vnode.props.type === 'number')
    // change 值改变且光标离开焦点时触发;input 一直触发
    addEventListener(el, lazy ? 'change' : 'input', e => {
      if ((e.target as any).composing) return
      let domValue: string | number = el.value
      if (trim) {
        // 如果 trim 则手动调用 trim
        domValue = domValue.trim()
      }
      if (castToNumber) {
        // 如果是数字,则调用toNumber转为数字
        domValue = looseToNumber(domValue)
      }
      el[assignKey](domValue)
    })
    if (trim) {
      addEventListener(el, 'change', () => {
        el.value = el.value.trim()
      })
    }
    if (!lazy) {
      // 处理中文输入时,用户输入值但未按空格确定时触发的问题
      addEventListener(el, 'compositionstart', onCompositionStart)
      addEventListener(el, 'compositionend', onCompositionEnd)
      // Safari < 10.2 & UIWebView doesn't fire compositionend when
      // switching focus before confirming composition choice
      // this also fixes the issue where some browsers e.g. iOS Chrome
      // fires "change" instead of "input" on autocomplete.
      addEventListener(el, 'change', onCompositionEnd)
    }
  },
  // set value on mounted so it's after min/max for type="range"
  // 赋值 value, 目前只是单向流动
  mounted(el, { value }) {
    el.value = value == null ? '' : value
  },
  beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
    el[assignKey] = getModelAssigner(vnode)
    // avoid clearing unresolved text. #2302
    if ((el as any).composing) return
    if (document.activeElement === el && el.type !== 'range') {
      if (lazy) {
        return
      }
      if (trim && el.value.trim() === value) {
        return
      }
      if (
        (number || el.type === 'number') &&
        looseToNumber(el.value) === value
      ) {
        return
      }
    }
    const newValue = value == null ? '' : value
    if (el.value !== newValue) {
      el.value = newValue
    }
  }
}

这段代码定义了一个名为 vModelText 的 Vue.js 自定义指令,用于处理文本输入框和文本区域的双向数据绑定(two-way data binding)。让我详细解释一下这段代码的各个部分:

  1. vModelText 的定义:

    • vModelText 是一个自定义指令,用于处理