• 【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
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

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

    1. vModelText 的定义:

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