vue源码分析(二十六)Vue之指令(v-model)解析mod

2020-04-21  本文已影响0人  vue爱好者

我们先打开文件src\compiler\parser

export default function model (
  el: ASTElement,
  dir: ASTDirective,
  _warn: Function
): ?boolean {
  warn = _warn
  const value = dir.value // v-model绑定的值
  const modifiers = dir.modifiers // 指令修饰符
  const tag = el.tag // 指令节点名称
  const type = el.attrsMap.type // input标签的type类型

  if (process.env.NODE_ENV !== 'production') {
    // input标签的类型不能是 type="file" 因为file的值是只读的,
    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') {
   // 标签不支持v-model指令
    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
}

下面我们对这个代码进行一分析:

 if (process.env.NODE_ENV !== 'production') {
    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']
      )
    }
  }

首先看到是input标签的类型进行了一个判断,不能是 type="file" 因为file的值是只读的,不能用代码进行赋值,只能赋值为空字符串来清空值。
紧接着是对各种input类型进行处理,我们本次的例子的type="text"默认的,所有我们主要是来分析genDefaultModel(el, value, modifiers)函数的处理。

我们来看看genDefaultModel的具体代码:

function genDefaultModel (
 el: ASTElement,
 value: string,
 modifiers: ?ASTModifiers
): ?boolean {
 const type = el.attrsMap.type //  input标签的type类型

 // warn if v-bind:value conflicts with v-model
 // except for inputs with v-bind:type
 if (process.env.NODE_ENV !== 'production') {
   const value = el.attrsMap['v-bind:value'] || el.attrsMap[':value']
   const typeBinding = el.attrsMap['v-bind:type'] || el.attrsMap[':type']
   if (value && !typeBinding) {
     const binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value'
     warn(
       `${binding}="${value}" conflicts with v-model on the same element ` +
       'because the latter already expands to a value binding internally',
       el.rawAttrsMap[binding]
     )
   }
 }
 // 这个就是v-model 指令的3个修饰符
 // lazy 监听change事件
 // number 将输入转换为数字
 // trim 过滤前后空格
 const { lazy, number, trim } = modifiers || {}
 const needCompositionGuard = !lazy && type !== 'range'
 const event = lazy
   ? 'change'
   : type === 'range'
     ? RANGE_TOKEN
     : 'input'
// valueExpression  计算value的表达式
 let valueExpression = '$event.target.value'
 if (trim) {
   valueExpression = `$event.target.value.trim()`
 }
 if (number) {
 // _n 就是一个toNumber函数,内部调用'parseFloat'进行强制类型转换
   valueExpression = `_n(${valueExpression})`
 }
 // 用于生成v-model指令的赋值代码
 let code = genAssignmentCode(value, valueExpression)
 if (needCompositionGuard) {
  // 输入的延时更新 https://zhuanlan.zhihu.com/p/55396545
   code = `if($event.target.composing)return;${code}`
 }
 // 给Prop数组添加值
 addProp(el, 'value', `(${value})`)
 // 添加事件处理
 addHandler(el, event, code, null, true)
 // 存在trim 和 number 修饰符的话,就强制更新
 if (trim || number) {
   addHandler(el, 'blur', '$forceUpdate()')
 }
}

我们来看看具体的代码分析:

if (process.env.NODE_ENV !== 'production') {
   const value = el.attrsMap['v-bind:value'] || el.attrsMap[':value']
   const typeBinding = el.attrsMap['v-bind:type'] || el.attrsMap[':type']
   if (value && !typeBinding) {
     const binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value'
     warn(
       `${binding}="${value}" conflicts with v-model on the same element ` +
       'because the latter already expands to a value binding internally',
       el.rawAttrsMap[binding]
     )
   }
 }

可以看到如果存在了v-model指令,就不要去绑定value值了,如果需要绑定value的值,那就需要绑定type类型。

最后会在生成DOM节点的时候调用addEventListener添加事件绑定。

上一篇下一篇

猜你喜欢

热点阅读