v-model
It 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-model
two 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
radio
so 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)
addHandler
is the method of binding events, and the second parameter is the name of the binding event. It can be seen that , select
, checkbox
and radio
are binding change
events , that is, change
the 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
input
event ; v-model
Iflazy
the modifier is used when binding, then the event it binds ischange
;- If there is
type="range"
an attribute , the bound event is__r
; - If there is
trim
an ornumber
modifier, one moreblur
event ;
// 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-model
When binding to a custom component, genComponentModel
the method . The main purpose of this method is to bind an model
object to el. The object includes value
, callback
, and expression
three 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 model
an option , and if there is, call transformModel
to 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
}
}
transformModel
It is mainly to determine the syntactic sugar of the custom component. If there is an option options
in model
, use the corresponding attribute in model
the option as the syntactic sugar. If there is no model
option, the default syntactic sugar is v-bind:value
and 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-model
bound to common form elementsselect
,checkbox
,radio
, the syntactic sugar isv-bind:value
andv-on:change
; - When
v-model
boundinput
totextarea
, there are several cases of syntactic sugar:- Defaults to
input
event ; - Events with
lazy
modifierschange
; - With
type="range"
attribute is__r
; - Add event with
trim
or ;number
blur
- Defaults to
- When
v-model
bound to a custom component, the syntactic sugar isv-bind:value
andv-on:input
or a custommodel
option;
You can verify the syntactic sugar of by online template compilation ;v-model