【Vue原理】Directive - 白話版

寫文章不容易,點個讚唄兄弟
專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧
研究基於 Vue版本 【2.5.17】

若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧node

【Vue原理】Directive - 白話版 數組

今天是除夕啦,你們新年快樂!身體健康!2019多多開花,耶,嗨起來!dom

言歸正傳!函數

我相信應該你們應該都使用過 Directive 指令,有時爲了可以直接操做DOM,而指令中最重要的莫過因而 鉤子函數了,指令一共有五個鉤子函數,他們不會在不一樣的階段觸發,文檔也已經說明性能

固然了,其實你只要瞭解它是何時觸發的,就徹底能夠用在項目。可是咱們是不會知足於此的,我要知道他是怎麼觸發的,怎麼調用到我設置的鉤子的學習

今天,咱們就來簡單說一下這幾個鉤子都是怎麼被調用的spa

你能相信我寫 Directive 花了一個星期啊,不是有多難,而是我不知道怎麼下手寫啊,根本不知道怎麼描述會簡單好了解,吐血3d


鉤子如何調用

首先,Vue 在綁定了指令的DOM 建立以後,插入頁面以前,對一些DOM 自己的事件或者屬性等進行處理。code

其中,就包含對本DOM的全部指令進行處理component

怎麼處理呢?每個鉤子函數都不同,因此咱們會分不一樣鉤子說明

首先,處理時,Vue 要判斷哪些指令是新的仍是舊的

因此須要比較 舊節點上的指令 和 新節點上的指令

好比 新指令比舊指令 多了一個指令 ,以下

// 新指令 newDir 以下

newDir={  

    "v-test":{        
        name: "test",    
        rawName: "v-test"
   }, 

    "v-test2":{        
        name:"test2"
        rawName:"v-test2"
    }  
}



// 舊指令 oldDir 以下,少了一個 v-test2
oldDir={  
    "v-test":{    
        name: "test",   
        rawName: "v-test"
    }  
}

若是是新指令

遍歷新指令對象時,當 'v-test2' 指令不存在 舊指令對象中,則表示這個是新指令,須要初始化

一、bind

遍歷遇到新指令時,直接執行 bind 鉤子函數,並傳入參數

for(i in newDir){    
    var dir = newDir[i]    
    if( !oldDir[i]){
        dir.bind(....參數)
    } 
}

二、inserted

按文檔的說明,咱們就知道,inserted 是在節點插入父節點調用

而全部節點的全部鉤子,會放在同一時間一塊兒處理,並非插入一個節點,就執行一個節點的 inserted 鉤子

inserted 分爲 保存 和 執行 兩個步驟

由於inserted 須要在 節點插入以後才執行,而如今處理是在 節點插入以前,因此只能先保存起來,用於後面執行。

簡單實現以下

// 一、使用一個數組 保存 本 DOM 的 全部新指令的 inserted 鉤子
var dirInserted = []

for(i in newDir){    
    var dir = newDir[i]    
    if( !oldDir[i]){
        dir.bind(....參數) 
       dirInserted.push(dir.inserted)
    }
}



// 二、新建一個函數,專門遍歷這個數組,逐個執行 inserted 鉤子
var callback = function(){    
    for(var i=0;i=dirInserted.length;i++){
        dirInserted[i](....參數)
    }
}



// 三、 把 callback 保存進當前節點的一個地方,爲了後面使用
dom.insert = callback

// 四、把 全部含有 callback 的節點,也放到一個數組
var insertedVnodeQueue=[]
if( 存在inserted 的 dom ){
    insertedVnodeQueue.push( dom )
}

// 五、當節點插入後,開始遍歷insertedVnodeQueue
for (var i = 0; i <insertedVnodeQueue.length; ++i) {
   insertedVnodeQueue[i].insert();
}

爲了驗證 inserted 鉤子 並非插入一個節點,就執行一次,我特意在 插入節點的函數後面打印一句話,因而能夠看到以下的打印順序

image

image

bind---> SPAN
插入DOM span

bind---> P
插入DOM p

bind---> DIV
插入DOM div

inserted---> SPAN
inserted---> P
inserted---> DIV

若是是舊指令

遍歷新指令對象時,當 v-test 指令也存在舊指令對象中,則表示這個是舊指令,須要更新

一、update

碰到舊指令,會直接執行 update 鉤子函數,並傳入參數

for(i in newDir){    
    var dir = newDir[i]    
    if( !oldDir[i]){
        dir.bind(....參數)
    }else{
        dir.update(...參數)
    } 
}

二、componentUpdated

componentUpdated 的保存方式 和 inserted 差很少,可是執行方式卻不同。

哎喲,這個鉤子是,更新一個節點,就立刻執行該節點鉤子的喔。跟 inserted 徹底不同喔,雖然我也不懂爲何

我也在源碼內更新 DOM 後加了一句打印,而後咱們看下執行順序

image

image

update---> DIV

update---> SPAN
完成更新DOM span
componentUpdated---> SPAN


update---> P
完成更新DOM p
componentUpdated---> P


完成更新DOM div
componentUpdated---> DIV

三、unbind

當 新指令 比 舊指令少了,好比下面這樣

// 舊指令
{  
    "v-test":{   
        name: "test",   
        rawName: "v-test"
    },    

    "v-test2":{     
        name:"test2"
        rawName:"v-test2"
    }  
}

// 新指令
{  

    "v-test":{        

        name: "test",    

        rawName: "v-test"
    }  
}

那麼 v-test2 指令被去掉了,這時確定會觸發 unbind 鉤子

unbind 鉤子也是直接調用的,沒有那麼多轉來轉去的邏輯幺蛾子,可是須要兩個判斷條件

一、不是新節點。剛剛建立的指令,指令確定都是新的,就不必往下走,浪費性能

二、某個舊指令 不存在 新指令對象中。證實這個指令已經被移除

if( 不是新節點 ){    
    for(i in oldDir){    
        var dir = oldDir[i]    
        if( !newDir[i]){  
            dir.unbind(....參數)    
        }  
    } 
}

公衆號

相關文章
相關標籤/搜索