v-model数据绑定分析

v-model数据绑定分析

v-model是Vue提供的指令,其主要作用是可以实现在表单<input>、<textarea>及<select>等元素以及组件上创建双向数据绑定,其本质上就是一种语法糖,既可以直接定义在原生表单元素,也可以支持自定义组件。在组件的实现中,可以配置子组件接收的prop名称,以及派发的事件名称实现组件内的v-model双向绑定。

描述

可以用v-model指令在表单<input>、<textarea>及<select>元素上创建双向数据绑定,其会根据控件类型自动选取正确的方法来更新元素,以<input>作为示例使用v-model。

<!DOCTYPE html> <html> <head> <title>Vue</title> </head> <body> <div></div> </body> <script src="http://cdn.bootcss.com/vue/2.4.2/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { msg: "" }, template: ` <div> <div>Message is: {{ msg }}</div> <input v-model="msg"> </div> ` }) </script> </html>

当不使用v-model语法糖时,可以自行实现一个双向绑定,实际上v-model在内部为不同的输入元素使用不同的property并抛出不同的事件:

input和textarea元素使用value property和input事件。

checkbox和radio元素使用checked property和change事件。

select元素将value作为prop并将change作为事件。

同样以<input>作为示例而不使用v-model实现双向绑定。

<!DOCTYPE html> <html> <head> <title>Vue</title> </head> <body> <div></div> </body> <script src="http://cdn.bootcss.com/vue/2.4.2/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { msg: "" }, template: ` <div> <div>Message is: {{ msg }}</div> <input :value="msg" @input="msg = $event.target.value"> </div> ` }) </script> </html>

对于v-model还有修饰符用以控制用户输入:

.trim: 输入首尾空格过滤。

.lazy: 取代input事件而监听change事件。

.number: 输入字符串转为有效的数字,如果这个值无法被parseFloat()解析,则会返回原始的值。

<!DOCTYPE html> <html> <head> <title>Vue</title> </head> <body> <div></div> </body> <script src="http://cdn.bootcss.com/vue/2.4.2/vue.js"></script> <script type="text/javascript"> var vm = new Vue({ el: "#app", data: { msg: 0 }, template: ` <div> <div>Message is: {{ msg }}</div> <div>Type is: {{ typeof(msg) }}</div> <input v-model.number="msg" type="number"> </div> ` }) </script> </html>

当使用自定义组件时,在组件上的v-model默认会利用名为value的prop和名为input的事件,但是像单选框、复选框等类型的输入控件可能会将value attribute用于不同的目的,此时可以使用model选项可以用来避免这样的冲突。

<!DOCTYPE html> <html> <head> <title>Vue</title> </head> <body> <div></div> </body> <script src="http://cdn.bootcss.com/vue/2.4.2/vue.js"></script> <script type="text/javascript"> Vue.component("u-input", { model: { prop: "message", event: "input" }, props: { message: { type: String }, }, template: ` <div> <input :value="message" @input="$emit('input', $event.target.value)"> </div> ` }) var vm = new Vue({ el: "#app", data: { msg: "" }, template: ` <div> <div>Message is: {{ msg }}</div> <u-input v-model="msg"></u-input> </div> ` }) </script> </html> 分析

Vue源码的实现比较复杂,会处理各种兼容问题与异常以及各种条件分支,文章分析比较核心的代码部分,精简过后的版本,重要部分做出注释,commit id为ef56410。

v-model属于Vue的指令,所以从编译阶段开始分析,在解析到指令之前,Vue的解析阶段大致流程:解析模版字符串生成AST、优化语法树AST、生成render字符串。

// dev/src/compiler/index.js line 11 export const createCompiler = createCompilerCreator(function baseCompile ( template: string, options: CompilerOptions ): CompiledResult { const ast = parse(template.trim(), options) // 生成AST if (options.optimize !== false) { optimize(ast, options) // 优化AST } const code = generate(ast, options) // 生成代码 即render字符串 return { ast, render: code.render, staticRenderFns: code.staticRenderFns } })

对指令的处理就在生成render字符串的过程,也就是generate函数的处理过程,在generate中调用genElement -> genData -> genDirectives,文章主要从genDirectives函数进行分析。

// dev/src/compiler/codegen/index.js line 43 export function generate ( ast: ASTElement | void, options: CompilerOptions ): CodegenResult { const state = new CodegenState(options) const code = ast ? genElement(ast, state) : '_c("div")' return { render: `with(this){return ${code}}`, // render字符串 staticRenderFns: state.staticRenderFns } } // dev/src/compiler/codegen/index.js line 55 export function genElement (el: ASTElement, state: CodegenState): string { // ... data = genData(el, state) // ... } // dev/src/compiler/codegen/index.js line 219 export function genData (el: ASTElement, state: CodegenState): string { // ... const dirs = genDirectives(el, state) // ... }

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

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