成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【Vue原理】VModel - 源碼版 之 表單元素綁定流程

sarva / 1681人閱讀

摘要:首先,兄弟,容我先說(shuō)幾句涉及源碼很多,篇幅很長(zhǎng),我都已經(jīng)分了上下三篇了,依然這么長(zhǎng),但是其實(shí)內(nèi)容都差不多一樣,但是我還是毫無(wú)保留地給你了。

寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】

如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧

【Vue原理】VModel - 源碼版 之 表單元素綁定流程

今天講解 v-model 的源碼版。首先,兄弟,容我先說(shuō)幾句

v-model 涉及源碼很多,篇幅很長(zhǎng),我都已經(jīng)分了上下 三篇了,依然這么長(zhǎng),但是其實(shí)內(nèi)容都差不多一樣,但是我還是毫無(wú)保留地給你了。你知道我這篇文章寫(xiě)了多久,一個(gè)多星期啊,不是研究多久啊,是寫(xiě)啊寫(xiě)啊,不停地修修改改,一直在想如何才能講明白

如果你做好了十足的學(xué)習(xí)準(zhǔn)備,會(huì)對(duì)你事半功倍,如果你只是看看,請(qǐng)看白話版吧,不然估計(jì)會(huì)越看越煩.....

如果你看過(guò)白話版,估計(jì)你會(huì)了解今天內(nèi)容的大概,也能很快就入戲

今天講解不同表單元素的Vue是如何處理的,表單元素有

input、textarea、select、checkbox、radio 五大種

所以,我們把每個(gè)表單元素當(dāng)做一個(gè)模塊,然后每個(gè)模塊解決三個(gè)問(wèn)題的流程,來(lái)開(kāi)始我們今天的表演

1、v-model 如何綁定表單值

2、v-model 如何綁定事件

4、v-model 如何雙向更新

TIP

下面所有涉及到的源碼,為了方便理解,都是簡(jiǎn)化過(guò)的,因?yàn)樵创a太長(zhǎng),所以只保留主要思想,去掉了很多兼容處理以及錯(cuò)誤處理

v-model 指令的處理

我們現(xiàn)在假設(shè)模板的解析已經(jīng)到了 解析 v-model 的部分....

Vue 會(huì)調(diào)用 model 方法 來(lái)解析 v-model ,這個(gè)方法里面,針對(duì)不同的表單元素,再調(diào)用不同的專屬方法進(jìn)行深度解析

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

你也看到了,上面每種表單元素都會(huì)使用一個(gè)方法來(lái)特殊照顧,不過(guò)這些方法,作用大致一樣

1、給表單元素設(shè)置綁定值

2、給表單元素設(shè)置事件及回調(diào)

所以這里,我們把方法的都設(shè)計(jì)到的方法以及流程說(shuō)一下

插播上面的el 是什么?

el 是 ast,而我的理解就是解析模板后,用樹(shù)結(jié)構(gòu)來(lái)表示某個(gè)dom節(jié)點(diǎn),這里先不用深究,你就只要知道他是保存解析模板后所有的數(shù)據(jù),包括你綁定的事件,綁定的指令,綁定的屬性等等,一張圖看下

下面所有的處理都是以 el 為基礎(chǔ)的

表單元素設(shè)置綁定值

什么叫設(shè)置綁定值?

首先,比如你給表單元素設(shè)置 v-model ="name",name 是 內(nèi)部數(shù)據(jù)吧,所以要把 name 和 表單元素 兩個(gè)緊緊綁定起來(lái),方便后面進(jìn)行雙向更新

這里講的是每個(gè)表單元素綁定值的流程

他們都會(huì)調(diào)用 addProp 去保存綁定的屬性 然后 綁定屬性,流程一樣,所以提出來(lái)講,但是具體綁定什么屬性,每種元素都不盡相同,在下面表單元素模塊會(huì)詳解

1、調(diào)用 addProp,把 value 添加進(jìn) el.props

function addProp(el, name, value) {
    (el.props || (el.props = [])).push({ name: name, value: value });
}

2、接下來(lái)的解析,el.props 會(huì)拼接成進(jìn)字符串 domProps

function genData$2(el, state) {    
    var data = "{";    
    if (el.props) {

        data += "domProps:{" + (genProps(el.props)) + "},";
    }
    data = data.replace(/,$/, "") + "}";    
    return data
}

3、在插入 dom 之前,調(diào)用 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;
        }
    }
}

表單元素設(shè)置事件以及回調(diào)

這里講的是每個(gè)表單元素綁定事件的流程

1、拼接事件

每種元素拼接事件都不一樣,在下面表單元素模塊會(huì)詳解

2、保存事件名和拼接好的回調(diào)

每個(gè)元素的 event 事件 和 拼接的回調(diào)是不一樣,但是他們保存的流程都是一樣的,都會(huì)調(diào)用下面的方法,addHandler 去保存事件

下面 el 是dom 元素,event 是事件名,code 是拼接的回調(diào)

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

3、完善拼接回調(diào)

function genData$2(el) {    

    var data = "{";    
    if (el.events) {

        data += (genHandlers(el.events, false)) + ",";
    }
    data = data.replace(/,$/, "") + "}";    
    return data
}

genHandlers遍歷 el.event ,每一項(xiàng)的回調(diào)最外層包上一層 function 字符串,并把 所有事件 逐個(gè)拼接成 on 字符串

function genHandlers(events) {    
    var res = "on:{";    
    for (var name in events) {
        res += """ + name + "":" 
            + ("function($event){" 
            + (events[name].value) + ";}") + ",";
    }    
    return res.slice(0, -1) + "}"
}

轉(zhuǎn)接的 初始數(shù)據(jù)和結(jié)果 像下面這樣

4、綁定事件

在插入 dom 之前

會(huì)調(diào)用到 updateDOMListeners,把 上面保存到 on 的 所有事件, 遍歷綁定到 dom 上

updateDOMListeners 其實(shí)兜兜轉(zhuǎn)轉(zhuǎn)了很多方法 來(lái)處理,為了方便理解,已經(jīng)非常簡(jiǎn)化,但是意思是不變的

尤大:臥槽,我寫(xiě)幾百行,你濃縮成5行,你這是要向全國(guó)人民謝罪的啊

function updateDOMListeners(vnode) {    
    for (name in vnode.data.on) {
        vnode.elm.addEventListener(event, handler);
    }
}

下面所有例子使用這個(gè)vue實(shí)例,所有綁定 v-model 我都用 name

Input、Textarea

喲喲,看過(guò) model ,就知道 這兩種元素是使用 genDefaultModel 處理的

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

綁定值

看了上面的函數(shù),你就知道啦,input 和 textarea 調(diào)用 addProp 綁定的是 value

拼接事件

其實(shí)這里精煉就一句話,比 jio 簡(jiǎn)單

name = $event.target.value

但是呢!input 這里其實(shí)是很復(fù)雜的,比如兼容 range 啦,預(yù)輸入延遲更新啦 等等,但是現(xiàn)在我們不說(shuō)這些,放到下篇來(lái)講

然后,你能看到,input 和 textarea 一般綁定的是 input 事件,但是也有其他的處理,下篇講啦

編譯后的渲染 render 函數(shù)

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

雙向更新

我們可以看到上面的 render 執(zhí)行的時(shí)候,從實(shí)例讀取了 name,name 收集到 本組件 watcher

1、內(nèi)部變化,通知更新 watcher,render 重新執(zhí)行,獲取新的 name,綁定到 dom 元素屬性 value

2、外部變化,看上面的回調(diào)事件,可以知道直接把 $event.target.value 賦值給 內(nèi)部值name

Select

來(lái)看看 處理 select 的 genSelect 方法

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 并沒(méi)有在 genSelect 方法中調(diào)用addProp 綁定某個(gè)屬性

那么 select 在哪里設(shè)置了呢?Vue 專門使用了方法 setSelected 設(shè)置 selectedIndex,這個(gè)方法現(xiàn)在不說(shuō),你只要知道,他是更新 selectedIndex 的就好了,后面會(huì)有一篇專門說(shuō)

疑惑為什么 select 不像 input 一樣直接綁定 value,這樣,不是也可以確定選項(xiàng)嗎?

按我的理解呢,我覺(jué)得應(yīng)該是原始select的 value 只有字符串一類型的值,而 Vue 的select 支持 數(shù)字和字符串兩種類型的值啊

拼接事件

觀察下面的渲染函數(shù),就可以很清楚地名表,select 的回調(diào)是怎么一回事了

1、從所有option 中 篩選出被選擇的option

2、使用數(shù)組保存所有篩選后的option的value

3、判斷是否多選,多選返回?cái)?shù)組,單選返回?cái)?shù)組第一項(xiàng)

然后,你還能知道 select 綁定的是 change 事件

獻(xiàn)上 select 的渲染 render 函數(shù)

with(this) {    

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

雙向更新

render 執(zhí)行時(shí),directive 處從實(shí)例讀取了 name, name 收集到 本組件 watcher

1、內(nèi)部變化,通知更新 watcher,上面 render 重新執(zhí)行,獲取新name,于是更新 select 元素屬性 selectedIndex,于是select 當(dāng)前選項(xiàng)就改變了

2、外部變化,直接賦值給 綁定值,綁定值變化,通知 watcher 更新,更新完,重新設(shè)置 selectedIndex

Checkbox

genCheckboxModel 源碼奉上

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

}

綁定值

賦值給 checked

看上面的方法就知道啦,調(diào)用 addProps,設(shè)置 checked 值

拼接事件

哈哈,還是看下面的渲染函數(shù),看下 checkbox 的回調(diào),其實(shí)意思就是

1、數(shù)組,分是否選擇

a. 選擇,把當(dāng)前選項(xiàng) concat 進(jìn)數(shù)組

b. 取消選擇,把當(dāng)前選項(xiàng) 移除出數(shù)組

2、非數(shù)組,直接賦值

你還能知道 checkbox 綁定的是 change 事件

來(lái)看看checkbox 的渲染render函數(shù)

with(this) {    

    return _c("input", {        
        directives: [{            
            name: "model",            
            rawName: "v-model",            
            value: (name),            
            expression: "name"
        }],        

        attrs: {            
            "type": "checkbox",            
            "value": "1"
        },        

        domProps: {      
            // _i方法,作用是,判斷第二個(gè)參數(shù)是否在 第一個(gè)參數(shù)數(shù)組中      
            "checked": Array.isArray(name) ? _i(name, "1") > -1 : (name)

        },        
        on: {            
            "change": function($event) {    
                var $$a = name,
                $$el = $event.target,
                $$c = $$el.checked ? (true) : (false); 
                if (Array.isArray($$a)) {                    

                    var $$v = "1",
                    $$i = _i($$a, $$v);                    
                    if ($$el.checked) {
                        $$i < 0 && (name = $$a.concat([$$v]))
                    } 
                    else {
                        $$i > -1 && (name = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))
                    }
                }
                else {
                    name = $$c
                };
            }
        }
    })
}
Radio

處理 radio 元素的 genRadioModel 源碼

function genRadioModel(el, value) {    

    var valueBinding = el.value|| "null";

    addProp(el, "checked", 
        ("_q(" + value + "," + valueBinding + ")"));

    addHandler(el, "change", 
        `${value} = ${valueBinding}`, null, true);

}

怎么賦值

直接賦值給 checked,你看上面的方法調(diào)用 addProp 可以看到

拼接事件

這個(gè)真的更加簡(jiǎn)單了...比 input 還簡(jiǎn)單啊,都不用獲取值,只是直接賦值為 radio 的值

name="1", 1 是你設(shè)置給 radio 的value值

你還能知道 radio 綁定的是 change 事件

看下下面的radio 的渲染函數(shù)你就懂了

with(this) {    
    return _c("input", {        
        directives: [{            
            name: "model",            
            rawName: "v-model",            
            value: (name),            
            expression: "name"
        }],        
        attrs: {            
            "type": "radio",            
            "value": "1"
        },        

        domProps: {    
            // _q 方法,作用是,判斷兩個(gè)參數(shù)是否相等           
            "checked": _q(name, "1")
        },        
        on: {            
            "change": function($event) {
                name = "1";
            }
        }
    })
}

雙向更新

在 render 執(zhí)行的時(shí)候,綁定值 收集到 本組件的 watcher

1、內(nèi)部變化,通知更新 watcher,render 重新執(zhí)行,獲取新的 name,更新 radio 元素屬性 checked

2、外部變化,直接賦值 更新 綁定值 name 等于 radio元素屬性 value

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105203.html

相關(guān)文章

  • Vue原理VModel - 源碼 select 詳解

    摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之詳解今天我們來(lái)看看處 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 ...

    lsxiao 評(píng)論0 收藏0
  • Vue原理VModel - 白話

    摘要:執(zhí)行的時(shí)候,會(huì)綁定上下文對(duì)象為組件實(shí)例于是中的就能取到組件實(shí)例本身,的代碼塊頂層作用域就綁定為了組件實(shí)例于是內(nèi)部變量的訪問(wèn),就會(huì)首先訪問(wèn)到組件實(shí)例上。其中的獲取,就會(huì)先從組件實(shí)例上獲取,相當(dāng)于。 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得...

    keke 評(píng)論0 收藏0
  • Vue原理VModel - 源碼input詳解

    摘要:因?yàn)槭ソ裹c(diǎn)之后被強(qiáng)制更新了一波嗯,這就是的作用,把頁(yè)面上的顯示值也過(guò)濾一遍 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】VModel - 源碼版之input詳...

    leanxi 評(píng)論0 收藏0
  • Vue原理】Compile - 源碼 Parse 屬性解析

    摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之屬性解析哈哈哈,今天終 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...

    tinna 評(píng)論0 收藏0
  • Vue原理】Compile - 源碼 generate 節(jié)點(diǎn)數(shù)據(jù)拼接

    摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之節(jié)點(diǎn)數(shù)據(jù)拼接上一篇我們 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...

    fizz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

sarva

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<