1.v-model指令处理
// 这个el是ast, 使用树的结构来表示某个dom节点,包含了绑定的事件,指令,绑定的属性等
function model(el, dir) {
var value = dir.value;
var tag = el.tag;
var type = el.attrsMap.type;
if (tag === 'select') {
genSelect(el, value);
} else if (tag === 'input' && type === 'checkbox') {
genCheckboxModel(el, value);
} else if (tag === 'input' && type === 'radio') {
genRadioModel(el, value);
} else if (tag === 'input' || tag === 'textarea') {
genDefaultModel(el, value);
}
}
每种表单元素都会有一个方法来处理,不过这些方法作用大致一样
- 给表单元素绑定值
- 给表单元素设置事件以及回调
每个表单元素具体的绑定值的流程
- 调用
addProp
把value添加进el.props
function addProp(el, name, value) {
(el.props || (el.props = [])).push({ name: name, value: value });
}
- el.props会拼接成字符串
domProps
function genData$2(el, state) {
var data = '{';
if (el.props) {
data += "domProps:{" + (genProps(el.props)) + "},";
}
data = data.replace(/,$/, '') + '}';
return data
}
- 在插入dom之前,调用updateDOMProps, 把上面保存的domProps遍历赋值到dom上
function updateDOMProps(oldVnode, vnode) {
var props = vnode.data.domProps || {};
for (key in props) {
cur = props[key];
if (key === 'value') {
elm._value = cur;
elm.value = strCur;
}
else {
elm[key] = cur;
}
}
}
表单元素的事件绑定流程
- 拼接事件,每种元素拼接事件都不一样
- 保存事件名和拼接好回调,每个元素的event事件和拼接的回调不一样,但是它们保存的流程不一样,都会调用
addHandler
去保存事件
function addHandler(el, name, value) {
var events = el.events || (el.events = {});
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;
}
}
-
完善拼接回调
-
绑定回调,调用updateDOMListeners, 将上面保存到on的所有事件,遍历到dom上
function updateDOMListeners(vnode) {
for (name in vnode.data.on) {
vnode.elm.addEventListener(event, handler);
}
}
2.input textarea
function genDefaultModel(el, value, modifiers) {
var code = "if($event.target.composing)return;"
+ value + '=$event.target.value;';
addProp(el, 'value', ("(" + value + ")"));
addHandler(el, "input", code, null, true);
}
编译之后的render函数
with(this) {
return _c('input', {
directives: [{
name: "model",
rawName: "v-model",
value: (name),
expression: "name"
}],
attrs: {
"type": "text"
},
domProps: {
"value": (name)
},
on: {
"input": function($event) {
if ($event.target.composing) return;
name = $event.target.value;
}
}
})]
}
3. select
function genSelect(el, value, modifiers) {
var selectedVal = `
Array.prototype.filter.call($event.target.options,
function(o) {
return o.selected
})
.map(function(o) {
var val = \"_value\" in o ? o._value : o.value;
return + ('val')
})
${value} = '$event.target.multiple ? $$selectedVal : $$selectedVal[0]'
addHandler(el, 'change', code, null, true);`
}
select元素绑定的属性是selectedIndex,select的回调做了什么?
- 从所有的option中筛选出被选择的option
- 使用数组保存所有的筛选后的option的value
- 判断是否多选,多选返回数组,单选返回数组的第一项
return _c('select', {
directives: [{
name: "model",
rawName: "v-model",
value: (name),
expression: "name"
}],
on: {
"change": function($event) {
var $$selectedVal =
Array.prototype.filter
.call($event.target.options,function(o) {
return o.selected
})
.map(function(o) {
var val = "_value" in o ? o._value: o.value;
return val
});
name = $event.target.multiple ? $$selectedVal: $$selectedVal[0];
}
}
})
}
4.checkbox
function genCheckboxModel(el, value, modifiers) {
var valueBinding = el.value || 'null';
var trueValueBinding = el['true-value'] || 'true';
var falseValueBinding = el['false-value'] || 'false';
addProp(el, 'checked',
`Array.isArray(${value})?
_i(${value},${valueBinding})>-1
${trueValueBinding === 'true'?
":(" + value + ")" : ":_q(" + value + "," + trueValueBinding + ")"}`
);
addHandler(el, 'change',
`var $$a= ${value},
$$el=$event.target,
$$c = $$el.checked?(${trueValueBinding}):(${falseValueBinding});
if(Array.isArray($$a)){
var $$v= (${number? '_n(' + valueBinding+")":valueBinding}),
$$i = _i($$a,$$v);
if($$el.checked){
$$i<0&&(${value}=$$a.concat([$$v]))
}else{
$$i>-1&&(${value}=$$a.slice(0,$$i).concat($$a.slice($$i+1)))
}
}else{
${value} = $$c
}`,null, true
);
}
checkbox是赋值给checked
, checked的值还分为两种情况
- 如果是数组,就将选择的选项concat进数组,当取消选择是,把当前的选项移除数组
- 如果是非数组,那就直接赋值
5. Radio
function genRadioModel(el, value) {
var valueBinding = el.value|| 'null';
addProp(el, 'checked',
("_q(" + value + "," + valueBinding + ")"));
addHandler(el, 'change',
`${value} = ${valueBinding}`, null, true);
}