这篇文章主要讨论自定义组件的v-model
处理,会顺带提一提普通标签上的v-model
处理。v-model
算是比较复杂的一个内置指令了,主要是对于不同的宿主元素它需要不同的特殊处理,所以分支比较多。
parse
在parse
阶段,它是和自定义指令在processAttrs
函数内的同一个分支进行处理的,不同的是会多一个校验:
1 | function processAttrs(el) { |
checkForAliasModel
用于检查v-model
的参数是否是v-for
的迭代对象:
1 | function checkForAliasModel(el, value) { |
这个函数会去找是否有某个祖先元素存在v-for
,而不仅仅是父元素。
generate
同样和指令处理流程一致,会调用genDirectives
:
1 | function genDirectives(el: ASTElement, state: CodegenState): string | void { |
v-model
指令存在于state.directives
之中,所以gen
会被执行,v-model
的gen
位于src/platforms/web/compiler/directives/model.js
的model
函数:
1 | export default function model(el: ASTElement, dir: ASTDirective, _warn: Function): ?boolean { |
这里针对自定义组件、select
、checkbox
、radio
、textarea
做了特殊处理,我们的兴趣在自定义组件,其余的可以自己去了解,代码并不复杂。
可以看到针对自定义组件,model
函数返回的是false
,所以在genDirectives
中也不会把结果放到res
中,之后在patch
阶段也不会执行各种指令钩子函数。接下来看看genComponentModel
:
1 | /** |
光看这段代码有点抽象,况且还调用了其他子函数,最好使用测试代码打个断点看看。我们使用的测试代码是:
1 | <div id="app"><my-comp v-model="txt"></my-comp></div> |
执行完genComponentModel
后,el.model
添加的 3 个属性值为:
1 | el.model = { |
genAssignmentCode
子函数主要是处理绑定到v-model
的各种形式,如value、value.a、value['a']、value[0]
,这里不赘述。
之后在genData
中又会有专门的分支处理el.model
,这是为自定义组件准备的:
1 | export function genData(el: ASTElement, state: CodegenState): string { |
所以我们的测试代码最后给data
对象添加的属性值为:
1 | ` |
render 生成 vnode
自定义组件在创建vnode
对象时,会调用createComponent
方法(位于src/core/vdom/create-component.js
),其中会专门处理data.model
:
1 | export function createComponent( |
因为自定义组件可以定制v-model
的props
和event
名称,transformModel
就是来处理这种定制情形的:
1 | // transform component v-model info (value and callback) into |
在拿到prop
和event
的真正名称后,就会将data.model.value
和data.model.callback
赋值给data.props
和data.on
,之后v-model
的痕迹就消失了,到这里应该可以看出来v-model
其实只是一个语法糖。