这篇文章主要讨论自定义组件的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其实只是一个语法糖。