在vue中,v-model无疑是最经常使用的API之一了,像 input、textarea、radio、checkbox、select等均可以使用v-model实现双向绑定。那么它具体是怎么实现的呢? 下面经过本身写的一个demo来具体分析一下?html
在vue的parse阶段也就是将AST树生成code的过程当中会去收集元素上定义的指令vue
model = $event.target.value
复制代码
再回到genDefaultModel,接下来会执行两个很是重要的方法node
addProp(el, 'value', ("(" + value + ")"));
addHandler(el, event, code, null, true);
复制代码
第一个方法addProp实际上就是给咱们的input标签添加一个prop,至关于动态绑定了value, 第二个方法addHandler就是给input标签添加一个input事件,而且在事件触发的时候动态修改model的值。如此,也就实现了双向绑定。express
<input
v-bind:value="model"
v-on:input="model=$event.target.model">
复制代码
最后再回到genDirectives,会根据指令生成一个render函数bash
let baseSpan = {
template: '<div><span v-show="isShow">我是显示仍是不显示呢</span>' + '<button @click="changeValue">点击</button></div>',
props: ['value'],
name: 'baseSpan',
data(){
return {
isShow: this.value
}
},
methods: {
changeValue() {
this.isShow = true
this.$emit('input', true)
}
}
}
var app = new Vue({
el: '#app',
data: {
visible: false
},
components: {
baseSpan
}
})
Vue.component('base-span',baseSpan);
复制代码
html代码以下app
<div id='app'>
<base-span v-model='visible'></base-span>
<span>{{visible}}</span>
</div>
复制代码
父组件的visible数据关联到了子组件baseSpan v-model指令上,而子组件上定义了一个value的props,以及一个changeValue方法,方法内部经过派发一个input事件向父组件传递数据。 这是典型的父子组件通讯方式,也是v-model生效的必要条件。固然这个value和input并非写死的,咱们能够根据实际须要在子组件的model配置中进行自定义,好比,咱们派发的事件也能够是一个change。那么咱们来看下源码是如何设计的?函数
if (el.component) {
genComponentModel(el, value, modifiers);
// component v-model doesn't need extra runtime return false } 复制代码
组件的v-model一样执行指令的model函数,而后命中上面的if语句,进而执行genComponentModel(el, value, modifiers)方法。ui
function genComponentModel (
el,
value,
modifiers
) {
var ref = modifiers || {};
var number = ref.number;
var trim = ref.trim;
var baseValueExpression = '$$v';
var valueExpression = baseValueExpression;
if (trim) {
valueExpression =
"(typeof " + baseValueExpression + " === 'string'" +
"? " + baseValueExpression + ".trim()" +
": " + baseValueExpression + ")";
}
if (number) {
valueExpression = "_n(" + valueExpression + ")";
}
var assignment = genAssignmentCode(value, valueExpression);
el.model = {
value: ("(" + value + ")"),
expression: ("\"" + value + "\""),
callback: ("function (" + baseValueExpression + ") {" + assignment + "}")
};
}
复制代码
针对咱们这个例子而言会生成以下的el.modelthis
// component v-model
if (el.model) {
data += "model:{value:" + (el.model.value) + ",callback:" + (el.model.callback) + ",expression:" + (el.model.expression) + "},";
}
复制代码
这样父组件的render函数会包含子组件model的配置项,那么在建立子组件vnode阶段,会执行createComponent函数。 而后执行以下的逻辑spa
// transform component v-model data into props & events
if (isDef(data.model)) {
transformModel(Ctor.options, data);
}
复制代码
那么transformModel主要是将model绑定的数据visible添加到data.props中。而后在on中添加data.model.callback.
// transform component v-model info (value and callback) into
// prop and event handler respectively.
function transformModel (options, data) {
var prop = (options.model && options.model.prop) || 'value';
var event = (options.model && options.model.event) || 'input';(data.props || (data.props = {}))[prop] = data.model.value;
var on = data.on || (data.on = {});
if (isDef(on[event])) {
on[event] = [data.model.callback].concat(on[event]);
} else {
on[event] = data.model.callback;
}
}
复制代码
其实就至关于父组件将visible经过props传递给子组件的value。而后子组件改变数据的时候,经过派发事件通知父组件更新visible。经过demo演示就是:当我点击按钮的时候,isShow=true 子组件中的span显示。visible = true。
可见不管是表单元素的v-model仍是组件的v-model。他们的本质就是一种语法糖。
公司目前正在使用vue,闲暇之余本身写demo,经过单步调试的方法,一步一步的去理解vue源码。若有不正之处,还请不吝赐教。