美文网首页
vue源码分析(二十六)Vue之指令(v-model)解析mod

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

作者: vue爱好者 | 来源:发表于2020-04-21 20:53 被阅读0次

    我们先打开文件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添加事件绑定。

    相关文章

      网友评论

          本文标题:vue源码分析(二十六)Vue之指令(v-model)解析mod

          本文链接:https://www.haomeiwen.com/subject/aaqjuhtx.html