前言
在很多时候我们说道vue,都说vue是双向绑定来实现的,但这么说是不太准确的,vue其实是mvvm数据驱动视图,用数据来驱动视图,DOM的改变。而v-model可以说是vue对于双向绑定的一个语法糖,那接下来看看v-model的实现吧。
编译
在把我们传入的模板编译成AST树的阶段,并不会对v-model做过多的处理,而是直接将其放在AST节点的directives中,而对v-model的处理是在我们生成render函数的codegen阶段。
codegen
在生成代码的这个阶段,在genElement这个函数中
从这里可以看到他会对我们的子节点在进行一次遍历,然后在进行一次genElement函数,当我们的节点含有v-model这个指令的时候,会命中genData$2这个函数,进入这个函数对我们的v-model进行一些处理。
在genData中会执行genDirectives这个方法,对我们的指令进行一些处理,而v-model就是指令之一。所以进入这个方法一看。
他会遍历当中的directives然后对我们遍历的每一个directive进行相应的处理,编译成一个res的形式。
而在!!gen(el, dir, state.warn)这个函数中,他会去执行model这个函数。那我们看看model这个函数的实现。
也是一对判断逻辑,对我们model的一些场景进行一些判断,我们写的是简单的v-model的例子,所以他会直接进入genDefaultModel这个函数当中,我们进入这个函数看看model的处理。
前面是对我们v-model指令的一些属性的判断,我们是没有加lazy或者number或者trim的,所以就会执行默认的addProp函数和addHandler函数,而这两个函数就是我们v-model实现的地方了。
上面是addProp函数的实现,很简单,他就是给我们的el元素去添加一个props属性,就是给input表单添加一个value的值。
而在addHandler中。
function addHandler (
el,
name,
value,
modifiers,
important,
warn,
range,
dynamic
) {
modifiers = modifiers || emptyObject;
// warn prevent and passive modifier
/* istanbul ignore if */
if (
process.env.NODE_ENV !== 'production' && warn &&
modifiers.prevent && modifiers.passive
) {
warn(
'passive and prevent can\'t be used together. ' +
'Passive handler can\'t prevent default event.',
range
);
}
// normalize click.right and click.middle since they don't actually fire
// this is technically browser-specific, but at least for now browsers are
// the only target envs that have right/middle clicks.
if (modifiers.right) {
if (dynamic) {
name = "(" + name + ")==='click'?'contextmenu':(" + name + ")";
} else if (name === 'click') {
name = 'contextmenu';
delete modifiers.right;
}
} else if (modifiers.middle) {
if (dynamic) {
name = "(" + name + ")==='click'?'mouseup':(" + name + ")";
} else if (name === 'click') {
name = 'mouseup';
}
}
// check capture modifier
if (modifiers.capture) {
delete modifiers.capture;
name = prependModifierMarker('!', name, dynamic);
}
if (modifiers.once) {
delete modifiers.once;
name = prependModifierMarker('~', name, dynamic);
}
/* istanbul ignore if */
if (modifiers.passive) {
delete modifiers.passive;
name = prependModifierMarker('&', name, dynamic);
}
var events;
if (modifiers.native) {
delete modifiers.native;
events = el.nativeEvents || (el.nativeEvents = {});
} else {
events = el.events || (el.events = {});
}
var newHandler = rangeSetItem({ value: value.trim(), dynamic: dynamic }, range);
if (modifiers !== emptyObject) {
newHandler.modifiers = modifiers;
}
var handlers = events[name];
/* istanbul ignore if */
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;
}
el.plain = false;
}
他会对我们传入的handler事件进行一系列的判断,然后在把input事件绑定到v-model上。
所以对于我们的表单元素v-model的实现。其实就是一个语法糖,会给表单元素添加一个value的prop然后在添加一个input事件去监听我们的输入,然后再把值赋予value实现双向绑定。