v-model数据绑定分析 (2)

在生成AST阶段,也就是parse阶段,v-model被当做普通的指令解析到el.directives中,genDrirectives方法就是遍历el.directives,然后获取每一个指令对应的方法,对于v-model而言,在此处获取的是{name: "model", rawName: "v-model" ...},通过state找到model指令对应的方法model()并执行该方法。

// dev/src/compiler/codegen/index.js line 309 function genDirectives (el: ASTElement, state: CodegenState): string | void { const dirs = el.directives // 获取指令 if (!dirs) return let res = 'directives:[' let hasRuntime = false let i, l, dir, needRuntime for (i = 0, l = dirs.length; i < l; i++) { // 遍历指令 dir = dirs[i] needRuntime = true const gen: DirectiveFunction = state.directives[dir.name] // 对于v-model来说 const gen = state.directives["model"]; if (gen) { // compile-time directive that manipulates AST. // returns true if it also needs a runtime counterpart. needRuntime = !!gen(el, dir, state.warn) } if (needRuntime) { hasRuntime = true res += `{name:"${dir.name}",rawName:"${dir.rawName}"${ dir.value ? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}` : '' }${ dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : '' }${ dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : '' }},` } } if (hasRuntime) { return res.slice(0, -1) + ']' } }

model方法主要是根据传入的参数对tag的类型进行判断,调用不同的处理逻辑。

// dev/src/platforms/web/compiler/directives/model.js line 14 export default function model ( el: ASTElement, dir: ASTDirective, _warn: Function ): ?boolean { warn = _warn const value = dir.value const modifiers = dir.modifiers const tag = el.tag const type = el.attrsMap.type if (process.env.NODE_ENV !== 'production') { // inputs with type="file" are read only and setting the input's // value will throw an error. if (tag === 'input' && type === 'file') { warn( `<${el.tag} v-model="${value}" type="file">:\n` + `File inputs are read only. Use a v-on:change listener instead.`, el.rawAttrsMap['v-model'] ) } } // 分支处理 if (el.component) { genComponentModel(el, value, modifiers) // component v-model doesn't need extra runtime return false } else if (tag === 'select') { genSelect(el, value, modifiers) } else if (tag === 'input' && type === 'checkbox') { genCheckboxModel(el, value, modifiers) } else if (tag === 'input' && type === 'radio') { genRadioModel(el, value, modifiers) } else if (tag === 'input' || tag === 'textarea') { genDefaultModel(el, value, modifiers) } else if (!config.isReservedTag(tag)) { genComponentModel(el, value, modifiers) // component v-model doesn't need extra runtime return false } else if (process.env.NODE_ENV !== 'production') { warn( `<${el.tag} v-model="${value}">: ` + `v-model is not supported on this element type. ` + 'If you are working with contenteditable, it\'s recommended to ' + 'wrap a library dedicated for that purpose inside a custom component.', el.rawAttrsMap['v-model'] ) } // ensure runtime directive metadata return true }

genDefaultModel函数先处理了modifiers修饰符,其不同主要影响的是event和valueExpression的值,对于<input>标签event为input,valueExpression为$event.target.value,然后去执行genAssignmentCode去生成代码,以及添加属性值与事件处理。

// dev/src/platforms/web/compiler/directives/model.js line 127 function genDefaultModel ( el: ASTElement, value: string, modifiers: ?ASTModifiers ): ?boolean { const type = el.attrsMap.type // warn if v-bind:value conflicts with v-model // except for inputs with v-bind:type // value与v-model冲突则发出警告 if (process.env.NODE_ENV !== 'production') { const value = el.attrsMap['v-bind:value'] || el.attrsMap[':value'] const typeBinding = el.attrsMap['v-bind:type'] || el.attrsMap[':type'] if (value && !typeBinding) { const binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value' warn( `${binding}="${value}" conflicts with v-model on the same element ` + 'because the latter already expands to a value binding internally', el.rawAttrsMap[binding] ) } } // 修饰符处理 const { lazy, number, trim } = modifiers || {} const needCompositionGuard = !lazy && type !== 'range' const event = lazy ? 'change' : type === 'range' ? RANGE_TOKEN : 'input' let valueExpression = '$event.target.value' if (trim) { valueExpression = `$event.target.value.trim()` } if (number) { valueExpression = `_n(${valueExpression})` } let code = genAssignmentCode(value, valueExpression) if (needCompositionGuard) { code = `if($event.target.composing)return;${code}` } addProp(el, 'value', `(${value})`) addHandler(el, event, code, null, true) if (trim || number) { addHandler(el, 'blur', '$forceUpdate()') } } // dev/src/compiler/directives/model.js line 36 export function genAssignmentCode ( value: string, assignment: string ): string { const res = parseModel(value) if (res.key === null) { return `${value}=${assignment}` } else { return `$set(${res.exp}, ${res.key}, ${assignment})` } } 每日一题 https://github.com/WindrunnerMax/EveryDay 参考 https://cn.vuejs.org/v2/api/#v-model https://www.jianshu.com/p/19bb4912c62a https://www.jianshu.com/p/0d089f770ab2 https://cn.vuejs.org/v2/guide/forms.html https://juejin.im/post/6844903784963899400 https://juejin.im/post/6844903999414485005 https://segmentfault.com/a/1190000021516035 https://segmentfault.com/a/1190000015848976 https://github.com/haizlin/fe-interview/issues/560 https://ustbhuangyi.github.io/vue-analysis/v2/extend/v-model.html

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgxyj.html