之前有整理过Vue
响应式原理,响应式主要的效果是数据改变了就会引起页面修改。关于v-model
我们也不陌生,vue
的双向绑定指令,页面修改会引起数据修改,数据修改页面也会跟着改变。我们直到数据->页面是由vue
的响应式原理实现的,那么该怎么做到页面->数据的修改呢?
表单绑定
v-model
一般我们是在表单元素上进行使用,因为视图能影响数据,本质上是这个视图需要可交互,因此表单是实现这一交互的前提。表单的使用是以<input>
、<textarea>
、<select>
为核心。具体的使用细节这里就不一一细说了。
这里我们从模板解析开始分析,vue
对v-model
做了什么操作。
这里我们来看一些绑定在input
上的v-model
都经历了什么。
// 普通输入框
<input type="text" v-model="value1">
AST树的解析
模版的编译阶段,会调用var ast = parse(template.trim(), options)
生成AST
树,parse
函数的起他细节这里不展开分析,我们只说模板属性上的解析processAttrs
函数。
vue
模板属性有两部分组成,一部分是指令,另一部分是普通的html
标签属性。对于指令,出去v-on
和v-bind
,其他普通指令会执行addDirective
过程。
// 处理模板属性
function processAttrs(el) {
var list = el.attrsList;
var i, l, name, rawName, value, modifiers, syncGen, isDynamic;
for (i = 0, l = list.length; i < l; i++) {
name = rawName = list[i].name; // v-on:click
value = list[i].value; // doThis
if (dirRE.test(name)) {
// 1.针对指令的属性处理
···
if (bindRE.test(name)) {
// v-bind分支
···
} else if(onRE.test(name)) {
// v-on分支
···
} else {
// 除了v-bind,v-on之外的普通指令
···
// 普通指令会在AST树上添加directives属性
addDirective(el, name, rawName, value, arg, isDynamic, modifiers, list[i]);
if (name === 'model') {
checkForAliasModel(el, value);
}
}
} else {
// 2. 普通html标签属性
}
}
}
在AST
产生阶段对事件指令v-on
的处理是为AST
树添加events
属性。类似的,普通指令会在AST
树上添加directives
属性,我们可以看一下addDirective
函数
// 添加directives属性
function addDirective (el,name,rawName,value,arg,isDynamicArg,modifiers,range) {
(el.directives || (el.directives = [])).push(rangeSetItem({
name: name,
rawName: rawName,
value: value,
arg: arg,
isDynamicArg: isDynamicArg,
modifiers: modifiers // 模板中添加的修饰符,如:.lazy、.number、.trim
}, range));
el.plain = false;
}
最终AST
树上会多处一个属性对象
// AST
{
directives: {
{
rawName: 'v-model',
value: 'value',
name: 'v-model',
modifiers: undefined
}
}
}
render函数生成
render
函数生成阶段,generate
逻辑,其中genData
会对模版的各个属性进行处理,最终返回拼接好的字符串模板,而对指令的处理会进入genDirectives
函数
在genDirectives
函数中,会拿到之前AST
树中的directives
对象,并遍历解析指令对象,最终以'directives:['
包裹的字符串返回。
// directives render字符串的生成
function genDirectives (