# vue源码 v-model

# 本质

  • v-model会有对应的directives属性描述指令相关信息
  • genData /compiler/codegen/index.js
  • 是通过监听表单控件input事件去影响自身的value值
    <input v-model="value" type="text" />
    <input :value="value" @input="(e)=>{this.value = e.target.value}" type="text"/>
  • 父子组件通信的语法糖
    • 默认利用valuepropinput事件

# 源码相关

  • 对模版里面的各种属性进行处理并返回拼接好的字符串
export function genData (el: ASTElement, state: CodegenState): string {
  let data = '{'
...
// component v-model
  if (el.model) {
    data += `model:{value:${
      el.model.value
    },callback:${
      el.model.callback
    },expression:${
      el.model.expression
    }},`
  }
...
  • genDirectives 遍历解析指令对象,最终以directives:['..']包裹的字符串返回
function genDirectives (el: ASTElement, state: CodegenState): string | void {
 const dirs = el.directives
  if (!dirs) return
  let res = 'directives:['
  let hasRuntime = false
  let i, l, dir, needRuntime
  for (i = 0, l = dirs.length; i < l; i++) {
    dir = dirs[i]
    needRuntime = true
    const gen: DirectiveFunction = state.directives[dir.name]
...
  • model /compiler/directives/model.js
  • 对表单控件的ast树进行进一步的处理
export default function model (
  el: ASTElement,
  dir: ASTDirective,
  _warn: Function
): ?boolean {
  warn = _warn
  const value = dir.value
  const modifiers = dir.modifiers
  const tag = el.tag
  const type = el.attrsMap.type

  if (process.env.NODE_ENV !== 'production') {
    // inputs with type="file" are read only and setting the input's
    // value will throw an error.
    if (tag === 'input' && type === 'file') {
      warn(
        `<${el.tag} v-model="${value}" type="file">:\n` +
        `File inputs are read only. Use a v-on:change listener instead.`,
        el.rawAttrsMap['v-model']
      )
    }
  }

  if (el.component) {
    genComponentModel(el, value, modifiers)
    // component v-model doesn't need extra runtime
    return false
  } else if (tag === 'select') {
    genSelect(el, value, modifiers)
  } else if (tag === 'input' && type === 'checkbox') {
    genCheckboxModel(el, value, modifiers)
  } else if (tag === 'input' && type === 'radio') {
    genRadioModel(el, value, modifiers)
  } else if (tag === 'input' || tag === 'textarea') {
    genDefaultModel(el, value, modifiers) // 表单
  } else if (!config.isReservedTag(tag)) {
    genComponentModel(el, value, modifiers)
    // component v-model doesn't need extra runtime
    return false
  } else if (process.env.NODE_ENV !== 'production') {
    warn(
      `<${el.tag} v-model="${value}">: ` +
      `v-model is not supported on this element type. ` +
      'If you are working with contenteditable, it\'s recommended to ' +
      'wrap a library dedicated for that purpose inside a custom component.',
      el.rawAttrsMap['v-model']
    )
  }

  // ensure runtime directive metadata
  return true
}
  • genDefaultModel
...
  addProp(el, 'value', `(${value})`) // 添加value
  addHandler(el, event, code, null, true) // 添加事件
  if (trim || number) {
    addHandler(el, 'blur', '$forceUpdate()')
  }
  ...
Last Updated: 2021/9/22 下午7:44:44