Vue 表单元素绑定原理分析

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);
    }
}

每种表单元素都会有一个方法来处理,不过这些方法作用大致一样

  1. 给表单元素绑定值
  2. 给表单元素设置事件以及回调

每个表单元素具体的绑定值的流程

  1. 调用addProp把value添加进el.props
function addProp(el, name, value) {
    (el.props || (el.props = [])).push({ name: name, value: value });
}
  1. el.props会拼接成字符串domProps
function genData$2(el, state) {
    var data = '{';    
    if (el.props) {
        data += "domProps:{" + (genProps(el.props)) + "},";
    }
    data = data.replace(/,$/, '') + '}';    
    return data
}
  1. 在插入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;
        }
    }
}

表单元素的事件绑定流程

  1. 拼接事件,每种元素拼接事件都不一样
  2. 保存事件名和拼接好回调,每个元素的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;
    }
}
  1. 完善拼接回调

  2. 绑定回调,调用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的回调做了什么?

  1. 从所有的option中筛选出被选择的option
  2. 使用数组保存所有的筛选后的option的value
  3. 判断是否多选,多选返回数组,单选返回数组的第一项
    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的值还分为两种情况

  1. 如果是数组,就将选择的选项concat进数组,当取消选择是,把当前的选项移除数组
  2. 如果是非数组,那就直接赋值
5. Radio
function genRadioModel(el, value) {    
    var valueBinding = el.value|| 'null';
    addProp(el, 'checked', 
        ("_q(" + value + "," + valueBinding + ")"));
    addHandler(el, 'change', 
        `${value} = ${valueBinding}`, null, true);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值