Syntactic sugar for Vue2--v-model

v-modelIt is an instruction for two-way binding of data in vue. Internally, the two-way binding of data is actually completed through syntactic sugar. There are v-modeltwo forms of binding, one is bound to ordinary form elements, and the other is bound to It is fixed on the custom component, and the implementation of the two is slightly different;

They are bound to common form elements, respectively, and input select textarea radioso on . It can also be seen from the source code that their implementations are also slightly different. The difference is that the events triggered when changing values ​​are different;

1. Common form elements

Select, checkbox, and radio syntactic sugar correspond to v-bind:value="something"and v-on:change="something = $event.target.value"; when the source code performs instruction parsing, events will be bound to el, respectively as follows:

// src/platforms/web/compiler/directives/model.js
// select
addHandler(el, 'change', code, null, true)
// checkbox
addHandler(el, 'change',
           `var $$a=${
      
      value},` +
           '$$el=$event.target,' +
           `$$c=$$el.checked?(${
      
      trueValueBinding}):(${
      
      falseValueBinding});` +
           'if(Array.isArray($$a)){' +
           `var $$v=${
      
      number ? '_n(' + valueBinding + ')' : valueBinding},` +
           '$$i=_i($$a,$$v);' +
           `if($$el.checked){$$i<0&&(${
      
      genAssignmentCode(value, '$$a.concat([$$v])')})}` +
           `else{$$i>-1&&(${
      
      genAssignmentCode(value, '$$a.slice(0,$$i).concat($$a.slice($$i+1))')})}` +
           `}else{
     
     ${
      
      genAssignmentCode(value, '$$c')}}`,
           null, true
)
// radio
addHandler(el, 'change', genAssignmentCode(value, valueBinding), null, true)

addHandleris the method of binding events, and the second parameter is the name of the binding event. It can be seen that , select, checkboxand radioare binding changeevents , that is, changethe event will be triggered when the value changes;

There are several cases of grammatical sugar corresponding to input and textarea :

  • The default binding event is inputevent ;
  • v-modelIf lazythe modifier is used when binding, then the event it binds is change;
  • If there is type="range"an attribute , the bound event is __r;
  • If there is triman or numbermodifier, one more blurevent ;
// src/platforms/web/compiler/directives/model.js
export const RANGE_TOKEN = '__r'
const event = lazy
    ? 'change'
    : type === 'range'
      ? RANGE_TOKEN
      : 'input'
// ...其他代码
addHandler(el, event, code, null, true)
if (trim || number) {
    
    
    addHandler(el, 'blur', '$forceUpdate()')
}

2. Custom components

v-modelWhen binding to a custom component, genComponentModelthe method . The main purpose of this method is to bind an modelobject to el. The object includes value, callback, and expressionthree properties;

export function genComponentModel (
  el: ASTElement,
  value: string,
  modifiers: ?ASTModifiers
): ?boolean {
    
    
    // ...
    el.model = {
    
    
        value: `(${
      
      value})`,
        expression: JSON.stringify(value),
        callback: `function (${
      
      baseValueExpression}) {
     
     ${
      
      assignment}}`
    }
}

When creating a component, it will judge whether there is modelan option , and if there is, call transformModelto process;

// src/core/vdom/create-component.js
export function createComponent (
  Ctor: Class<Component> | Function | Object | void,
  data: ?VNodeData,
  context: Component,
  children: ?Array<VNode>,
  tag?: string
): VNode | Array<VNode> | void {
    
    
	// ...其他代码
    if (isDef(data.model)) {
    
    
        transformModel(Ctor.options, data)
	}
}

function transformModel (options, data: any) {
    
    
    // 有自定义的 model 选项,则使用自定义,否则默认语法糖为 value / input
    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
    }
}

transformModelIt is mainly to determine the syntactic sugar of the custom component. If there is an option optionsin model, use the corresponding attribute in modelthe option as the syntactic sugar. If there is no modeloption, the default syntactic sugar is v-bind:valueand v-on:input;

<!-- 自定义组件 -->
<script>
export default {
    name: 'customCom',
    props: {
        // v-model 绑定的值,默认应该为 value
        // 但是多了 model 选项中的 prop,所以可以通过 this.customValue 获取
      	customValue: {}  
    },
    model: {
        // 自定义 v-model 绑定的别名,必须有,否则依旧是 value 命名
        prop: 'customValue',
        // 修改绑定值时触发的事件
        event: 'customEvent'
    }
}
</script>

3. Summary:

  • When v-modelbound to common form elements select, checkbox, radio, the syntactic sugar is v-bind:valueand v-on:change;
  • When v-modelbound inputto textarea, there are several cases of syntactic sugar:
    • Defaults to inputevent ;
    • Events with lazymodifiers change;
    • With type="range"attribute is __r;
    • Add event with trimor ;numberblur
  • When v-modelbound to a custom component, the syntactic sugar is v-bind:valueand v-on:inputor a custom modeloption;

You can verify the syntactic sugar of by online template compilation ;v-model

Guess you like

Origin blog.csdn.net/Ljwen_/article/details/125400803