阿西河

所有教程

公众号
🌙
阿西河前端的公众号

我的收藏

    最近访问  (文章)

      教程列表

      抓包专区
      测试专区

      Vue.js 的v-model实现原理,及如何自定义v-model

      问题

      v-model中的实现原理及如何自定义v-model

      答案

      组件的v-modelvalue+input方法的语法糖

      <el-checkbox :value="" @input=""></el-checkbox>
      <el-checkbox v-model="check"></el-checkbox>
      

      可以自己重新定义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
          },
      })
      

      对应源码

      • 会将组件的v-model默认转化成value+input
      const VueTemplateCompiler = require('vue-template-compiler');
      const ele = VueTemplateCompiler.compile('<el-checkbox v-model="check"></el-checkbox>');
      // with(this) {
      //     return _c('el-checkbox', {
      //         model: {
      //             value: (check),
      //             callback: function ($$v) {
      //                 check = $$v
      //             },
      //             expression: "check"
      //         }
      //     })
      // }
      

      core/vdom/create-component.js line:155

      function transformModel (options, data: any) {
        const prop = (options.model && options.model.prop) || 'value'
        const event = (options.model && options.model.event) || 'input'
        ;(data.attrs || (data.attrs = {}))[prop] = data.model.value
        const on = data.on || (data.on = {})
        const existing = on[event]
        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
        }
      }
      
      • 原生的 v-model,会根据标签的不同生成不同的事件和属性
      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
                  }
              }
          })
      }
      */
      
      

      编译时:不同的标签解析出的内容不一样 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
        }
      

      运行时:会对元素处理一些关于输入法的问题 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
              }
            }
          }
        }
      

      更多面试题

      如果你想了解更多的前端面试题,可以查看本站的WEB前端面试题 ,这里基本包涵了市场上的所有前端方面的面试题,也有一些大公司的面试图,可以让你面试更加顺利。

      面试题
      HTMLCSSJavaScript
      jQueryVue.jsReact
      算法HTTPBabel
      BootStrapElectronGulp
      Node.js前端经验相关前端综合
      Webpack微信小程序-

      这些题库还在更新中,如果你有不错的面试题库欢迎分享给我,我整理后放上来;人人为我,我为人人,互帮互助,共同提高,祝大家都拿到心仪的Offer!

      目录
      目录