v-model实现原理是什么?v-model的使用方法介绍(附代码)
v-model实现原理
genDirectives
Vue初始化组件时通过genDirectives(el,state)初始化指令。(这里的el已经通过parseHTML将html结构转换成Vue的AST语法树,state是根据用户定义组件的options新建的CodegenState对象)。
<div id="test"> 请输入:<input type="text" v-model="message"><br/> 你输入的是:<input type="text" v-model="message" disabled > </div>
当用户在html页面上写了v-model指令进行数据双向绑定,Vue通过state找到model指令对应的方法model(el,dir,_warn),并执行该方法。(这里双!!表示将函数返回结果转换成Boolean类型)
var gen = state.directives[dir.name]; if (gen) { needRuntime = !!gen(el, dir, state.warn); }
model
if (el.component) { genComponentModel(el, value, modifiers); 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); return false } else { warn$1( "<" + (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.' ); }
model()根据表单元素的tag标签以及type属性的值,调用不同的方法也就验证了官网所说的“随表单控件类型不同而不同。”这里调用的就是genDefaultModel().
genDefaultModel
var type = el.attrsMap.type;
获取表单元素的类型,此处type='text'.
{ var value$1 = el.attrsMap['v-bind:value'] || el.attrsMap[':value']; var typeBinding = el.attrsMap['v-bind:type'] || el.attrsMap[':type']; if (value$1 && !typeBinding) { var binding = el.attrsMap['v-bind:value'] ? 'v-bind:value' : ':value'; warn$1( binding + "=\"" + value$1 + "\" conflicts with v-model on the same element " + 'because the latter already expands to a value binding internally' ); } }
检测该表单元素是否同时有v-model绑定和v-bind:value。
var ref = modifiers || {}; var lazy = ref.lazy; var number = ref.number; var trim = ref.trim; var needCompositionGuard = !lazy && type !== 'range'; var event = lazy ? 'change' : type === 'range' ? RANGE_TOKEN : 'input';
获取修饰符lazy,number及trim.
.lazy 取代input监听change事件
.number 输入字符串转为数字
.trim 输入首尾空格过滤
var valueExpression = '$event.target.value'; if (trim) { valueExpression = "$event.target.value.trim()"; } if (number) { valueExpression = "_n(" + valueExpression + ")"; }
定义变量valueExpression,本例子的情况valueExpression为'$event.target.value'。
var code = genAssignmentCode(value, valueExpression);
function genAssignmentCode ( value, assignment ) { var res = parseModel(value); if (res.key === null) { return (value + "=" + assignment) } else { return ("$set(" + (res.exp) + ", " + (res.key) + ", " + assignment + ")") } }
通过genAssignmentCode()方法生成v-model value值得代码。根据本文的例子返回的结果就是"message=$event.target.value"。
if (needCompositionGuard) { code = "if($event.target.composing)return;" + code; }
添加event.target.composing判断。event.target.composing用于判断此次input事件是否是IME构成触发的,如果是IME构成,直接return。IME 是输入法编辑器(Input Method Editor) 的英文缩写,IME构成指我们在输入文字时,处于未确认状态的文字。
带下划线的ceshi就属于IME构成,它会同样会触发input事件,但不会触发v-model更新数据。
addProp(el, 'value', ("(" + value + ")"));
function addProp (el, name, value) { (el.props || (el.props = [])).push({ name: name, value: value }); el.plain = false; }
给el添加prop
addHandler(el, event, code, null, true);
function addHandler (el,name,value,modifiers,important,warn){ /*其他代码省略*/ var newHandler = { value: value.trim() }; var handlers = events[name]; if (Array.isArray(handlers)) { important ? handlers.unshift(newHandler) : handlers.push(newHandler); } else if (handlers) { events[name] = important ? [newHandler, handlers] : [handlers, newHandler]; } else { events[name] = newHandler; } }
将code作为el的对应event处理的方法handler,此处events['input']=if($event.target.composing)return;message =$event.target.value
最后原来的html结构就由:
<input type="text" v-model="message">
变成了:
<input type="text" :value="message" @input="if($event.target.composing)return;message =$event.target.value">
添加修饰符
如果添加了trim修饰符
原来的html结构就由:
<input type="text" v-model.trim="message">
变成了:
<input type="text" :value="message" @input="if($event.target.composing)return;message =$event.target.value.trim()">
如果添加了lazy修饰符
原来的html结构就由:
<input type="text" v-model.lazy="message">
变成了:
<input type="text" :value="message" @change="if($event.target.composing)return;message =$event.target.value">
如果添加了number修饰符
原来的html结构就由:
<input type="text" v-model.number="message">
变成了:
<input type="text" :value="message" @change="if($event.target.composing)return;message = _n($event.target.value)">
这里的_n是在installRenderHelpers里面定义的,指向toNumber方法。
function installRenderHelpers (target) { /*其他代码省略*/ target._n = toNumber; }
而toNumber就是使用parseFloat函数来转的数字,再使用isNaN判断转换结果,如果结果是NaN,那么就返回原字符串,否则返回转为数字后的结果:
function toNumber (val) { var n = parseFloat(val); return isNaN(n) ? val : n }
注意:number修饰符不能限制输入的内容,就算输入的不是数字,也可能会被转换,如输入'1次测试',转换的结果就是1
相关文章推荐:
以上就是v-model实现原理是什么?v-model的使用方法介绍(附代码)的详细内容,更多请关注其它相关文章!