v-model的实现,是语法糖,但又不完全等于bind+on

84 阅读1分钟

从文档上可以看到介绍说,v-model等价于下面这种形式。

<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

去SFC跑了一下

<script setup>
import { ref } from 'vue'

const msg = ref('Hello World!')
const searchText = ref('h')
</script>

<template>
  <h1>{{ msg }}</h1>
<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>
<input v-model="searchText" />

</template>

结果:

return (_ctx, _cache) => {
  return (_openBlock(), _createElementBlock(_Fragment, null, [
    _createElementVNode("h1", null, _toDisplayString(msg.value), 1 /* TEXT */),
    _createElementVNode("input", {
      value: searchText.value,
      onInput: _cache[0] || (_cache[0] = $event => (searchText.value = $event.target.value))
    }, null, 40 /* PROPS, HYDRATE_EVENTS */, _hoisted_1),
    _withDirectives(_createElementVNode("input", {
      "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ((searchText).value = $event))
    }, null, 512 /* NEED_PATCH */), [
      [_vModelText, searchText.value]
    ])
  ], 64 /* STABLE_FRAGMENT */))
}
}

可以看到其实并不相等,跑去看了源码后发现主要因为v-model其实还有别的逻辑:

created(el, { modifiers: { lazy, trim, number } }, vnode) {
    el._assign = getModelAssigner(vnode)
    const castToNumber =
      number || (vnode.props && vnode.props.type === 'number')
    addEventListener(el, lazy ? 'change' : 'input', e => {
      if ((e.target as any).composing) return
      let domValue: string | number = el.value
      if (trim) {
        domValue = domValue.trim()
      }
      if (castToNumber) {
        domValue = looseToNumber(domValue)
      }
      el._assign(domValue)
    })
    if (trim) {
      addEventListener(el, 'change', () => {
        el.value = el.value.trim()
      })
    }

比如,后面的修饰符,以及在beforeupdate里做了点优化:

beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) {
    el._assign = 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
    }
  }