Vue2--v-model 的语法糖
v-model 语法糖
v-model
是 vue 中进行数据双向绑定的指令,在内部实际上是通过语法糖来完成数据的双向绑定,v-model
绑定的形式有两种,一种是绑定在普通表单元素上,一种是绑定在自定义组件上,两者在实现上也略微不同;
绑定在普通表单元素上,分别有 input
select
textarea
radio
等,从源码中也可以看出,他们之间的实现也略微不同,区别在于改变值时触发的事件不同;
一、普通表单元素
select、checkbox、radio 语法糖对应的是 v-bind:value="something"
和 v-on:change="something = $event.target.value"
;在源码进行指令解析时会给 el 绑定事件,分别如下:
// 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
是绑定事件的方法,第二个参数是绑定事件的名称,可以看出, select
、checkbox
、radio
是绑定的 change
事件,也就是值改变时会触发 change
事件;
input、textarea 对应的语法糖有几种情况:
- 默认绑定事件为
input
事件; - 如果在
v-model
绑定时用了lazy
修饰符,那么它绑定的事件是change
; - 如果有
type="range"
属性,则绑定的事件是__r
; - 如果有
trim
或者number
修饰符,则会多绑定一个blur
事件;
// 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()')
}
二、自定义组件
v-model
绑定在自定义组件时,调用的是 genComponentModel
方法,该方法主要的目的是在 el 身上绑定一个 model
对象,对象包括 value
、callback
、expression
三个属性;
export function genComponentModel (
el: ASTElement,
value: string,
modifiers: ?ASTModifiers
): ?boolean {
// ...
el.model = {
value: `(${value})`,
expression: JSON.stringify(value),
callback: `function (${baseValueExpression}) {${assignment}}`
}
}
在创建组件会判断是否有 model
选项,有则调用 transformModel
进行处理;
// 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
主要是确定自定义组件的语法糖,如果自定义组件的 options
里有 model
选项,则使用 model
选项里面对应的属性作为语法糖,如果没有 model
选项,则默认语法糖是 v-bind:value
和 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>
三、总结:
- 当
v-model
绑定在普通表单元素select
、checkbox
、radio
时,语法糖为v-bind:value
和v-on:change
; - 当
v-model
绑定在input
、textarea
时,语法糖分几种情况:- 默认为
input
事件; - 带
lazy
修饰符时为change
事件; - 带
type="range"
属性时为__r
; - 带
trim
或number
时新增blur
事件;
- 默认为
- 当
v-model
绑定在自定义组件时,语法糖为v-bind:value
和v-on:input
或者自定义model
选项;
可以通过在线模板编译来验证一下 v-model
的语法糖;
更多推荐
所有评论(0)