在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源碼。若有不正之處,還請不吝賜教。