vue核心面试题:v-model中的实现原理及如何自定义v-model

一、概念

1.v-model可以看成是value+input方法的语法糖。

2.组件的v-model就是value+input方法的语法糖。

3.可以绑定v-model的有:input,checkbox,select,textarea,radio 

二、源码

1.组件的 v-model源码

组件的 v-model 是默认转化成value+input

总结:

      在组件初始化的时候,回去判断当前数据中有没有model属性,如果有就会使用transformModel方法进行model的转换,transformModel会对数据的model属性进行判断,如果没有prop属性默认就是value,如果没有event属性就是input,然后将值赋给value,给on绑定input的事件,对于的就是callback。

       自定义model时,只需要在组件中传入model的prop和event属性。

const VueTemplateCompiler = require('vue-template-compiler');
const ele = VueTemplateCompiler.compile('<el-checkbox v-model="check"></elcheckbox>');
// 解析后
with(this) {
    return _c('el-checkbox', {
        model: { // v-model解析后
            value: (check), 
            callback: function ($$v) {
                check = $$v
            }, 
            expression: "check"
        }
    })
}

 (1)组件初始化时,会判断是否有model属性。(src/core/vdom/create-component.js)

           在createComponent方法中

// 如果当前数据有model属性,就会使用transformModel转换model
if (isDef(data.model)) {
   transformModel(Ctor.options, data)
}

(2)transformModel 方法用来转换model(src/core/vdom/create-component.js)

function transformModel (options, data: any) {
  // 如果没有prop属性默认就是value
  const prop = (options.model && options.model.prop) || 'value'
  // 如果没有event属性,默认就是input
  const event = (options.model && options.model.event) || 'input'
  ;(data.attrs || (data.attrs = {}))[prop] = data.model.value// data.attrs.value="xxx"
  const on = data.on || (data.on = {})
  const existing = on[event] // 给on绑定input的事件,对应的函数就是callback
  const callback = data.model.callback
  if (isDef(existing)) {
    if (
      Array.isArray(existing)
        ? existing.indexOf(callback) === -1
        : existing !== callback
    ) {
      on[event] = [callback].concat(existing)
    }
  } else {
    on[event] = callback // 
  }
}

(3)自定义v-model

Vue.component('el-checkbox', {
    template:`<input type="checkbox" :checked="check" @change="$emit('change',$event.target.checked)">`, 
    model:{
        prop:'check', // 更改默认的value的名字
        event:'change' // 更改默认的方法名
    },
    props: {
        check: Boolean
    }
})
 

2.原生的 v-model

原生的 v-model ,会根据标签的不同生成不同的事件和属性

总结:

       如果是普通标签,它一看是input就会直接绑成domProps,值是value,把on绑定input,在除了valueinput外还会加上一个指令directives,如果input上面写上type是checkbox时,这时候domProps就会变成checked,on的事件就会变成change,它在编译的时候会根据type生成不同的事件和属性。

      在编译的时候,如果有v-model,就会执行这个指令,找对指令下对应的model,就会对他进行判断,根据不同的类型生成不同的内容:

    组件---------->genComponentModel方法

    select-------->genSelect方法------------------>添加value属性,change事件

    checkbox--->genCheckboxModel方法----->添加checked属性,change事件

    radio--------->genRadioModel方法----------->添加checked属性,change事件

    textarea ---->genDefaultModel方法--------->添加value属性,input事件

    input--------->genDefaultModel方法--------->添加value属性,input或change事件,如果lazy是ture就是change

     在编译后多出一个directives指令是,是在运行时,在inserted方法中会对元素处理一些关于输入法的问题,对它增加了compositionstart,compositionend,change三个事件

compositionstart:当用户使用拼音输入法开始输入汉字时,这个事件就会被触发。

compositionend:将被触发 (具有特殊字符的触发, 需要一系列键和其他输入, 如语音识别或移动中的字词建议)。

<input v-model="sth" />
//  等同于
<input :value="sth" @input="sth = $event.target.value" />
const VueTemplateCompiler = require('vue-template-compiler');
const ele = VueTemplateCompiler.compile('<input v-model="value"/>');

with(this) {
    return _c('input', {
        directives: [{
            name: "model",
            rawName: "v-model",
            value: (value),
            expression: "value"
        }],
        domProps: {
            "value": (value)
        },
        on: {
            "input": function ($event) {
                if ($event.target.composing) return;
                value = $event.target.value
            }
        }
    })
}

// 将input添加上type,值是ckeckbox
const ele = VueTemplateCompiler.compile('<input type="checkbox" v-model="value"/>'); 
 // 编译完后domProps的属性就是checked,on绑定的就是change事件

(1)编译时:不同的标签解析出的内容不一样 (src/platforms/web/compiler/directives/model.js)

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']
    )
  }

(2)运行时:会对元素处理一些关于输入法的问题 (src/platforms/web/runtime/directives/model.js)

inserted (el, binding, vnode, oldVnode) {
    if (vnode.tag === 'select') {
      // #6903
      if (oldVnode.elm && !oldVnode.elm._vOptions) {
        mergeVNodeHook(vnode, 'postpatch', () => {
          directive.componentUpdated(el, binding, vnode)
        })
      } else {
        setSelected(el, binding, vnode.context)
      }
      el._vOptions = [].map.call(el.options, getValue)
    } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
      el._vModifiers = binding.modifiers
      if (!binding.modifiers.lazy) {
       // 对输入法做了一些校验,然后绑定自己的事件
        el.addEventListener('compositionstart', onCompositionStart) 
        el.addEventListener('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.
        el.addEventListener('change', onCompositionEnd)
        /* istanbul ignore if */
        if (isIE9) {
          el.vmodel = true
        }
      }
    }
  }

 

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
v-model实现原理是通过使用双向数据绑定的方式来实现的。在Vue.js,v-model指令会将表单元素的value属性和Vue实例的data属性进行双向绑定。当用户在表单元素输入内容时,v-model会将输入的值实时更新到Vue实例的data属性上,同时也会将data属性的值反映到表单元素的value属性上。这样就实现了数据的双向同步。 在Vue 2.x版本,双向数据绑定是通过Object身上的一个方法实现的响应式。Vue会使用Object.defineProperty方法劫持data对象的属性,使其变成响应式的,当属性值发生变化时,会触发相应的更新。 而在Vue 3,使用了Proxy代理来实现响应式。Proxy可以代理整个对象,并在对象的读取和修改时进行拦截。当修改被拦截时,Vue会检测到数据的变化并触发更新。 综上所述,v-model实现原理是通过双向数据绑定,将表单元素的value属性和Vue实例的data属性进行双向绑定,从而实现数据的同步更新。在Vue 2.x使用Object.defineProperty方法实现响应式,在Vue 3使用Proxy代理实现响应式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [vue项目v-model父子组件通信的实现详解](https://download.csdn.net/download/weixin_38642735/14807980)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【面试题】谈一下v-model实现原理](https://blog.csdn.net/admin_2022/article/details/130485222)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值