本節(jié)我們的示例如下
app.vue
child.vue
當(dāng)點(diǎn)擊按鈕缀旁,切換flag的值峭竣,此時(shí)將觸發(fā)get收集依賴并觸發(fā)set向dep分別notify粒督,這將在下一個(gè)tick中觸發(fā)更新彼棍,執(zhí)行patch娃惯,接收新舊兩個(gè)vnode
上一節(jié)跷乐,我們分析過,組件更新的分界點(diǎn)為sameVnode
? ??當(dāng)前組件未定義key趾浅、tag為main愕提、非注釋節(jié)點(diǎn)、都有data定義潮孽。故return true揪荣。調(diào)用patchVnode,入?yún)椋号f的vnode往史、新的vnode仗颈、[]
? ??用紅色框框圈出的位置,是針對組件而言椎例。vue在render過程中遇到子組件會調(diào)用createComponent方法挨决,該方法向組件data上掛載了hook(init、prepatch订歪、insert脖祈、destory);postpatch和update則是mergeVNodeHook時(shí)掛載的刷晋。在本次patch流程中的根vnode是app.vue的盖高,故先跳過(step:1)不看
? ? 每次render都會生成一個(gè)vnode慎陵,其children則保留著根節(jié)點(diǎn)的html元素對應(yīng)的vnode。故oldCh和ch拿到的都是組件vnode喻奥、空文本節(jié)點(diǎn)vnode席纽、button按鈕對應(yīng)的vnode。調(diào)用isPatchable
? ??????????根據(jù)之前分析得知撞蚕,vnode.componentInstance是在組件render過程中調(diào)用data.hook.init時(shí)緩存的润梯,換言之,只有組件的vnode有componentInstance值甥厦,那么同樣的纺铭,當(dāng)前的patch流程不是組件child,故不會進(jìn)入while循環(huán)刀疙,返回true
? ??????返回patchVnode舶赔,調(diào)用cbs.update。該方法是在createPatchFunction過程中保存的hooks鍵('create',?'activate',?'update',?'remove',?'destroy')庙洼,對應(yīng)的值為createPatchFunction傳入的modules顿痪,即各個(gè)模塊對應(yīng)的創(chuàng)建或更新的鉤子函數(shù),如updateAttrs
? ??????????????進(jìn)入updateAttrs函數(shù)油够,對在標(biāo)簽上定義的屬性值進(jìn)行比對更新
? ??????????????對attrs(id="app")進(jìn)行更新,我們這里兩次的id值是相同的蚁袭,假設(shè)id的值發(fā)生了變化,則調(diào)用setAttr
因此得出結(jié)論揩悄,cbs.update實(shí)際上就是針對dom的各個(gè)模塊調(diào)用原生的一些dom方法進(jìn)行比對更新
? ? ? ? ? ? ??
????????返回patchVnode,調(diào)用hook上的update針對組件做一些更新鬼悠,同樣的删性,由于當(dāng)前patch流程非組件vnode,故跳過
?????????代碼向下焕窝,判斷vnode.text是否存在蹬挺。如果該值存在,則說明該組件只有一個(gè)文本節(jié)點(diǎn)它掂,因此只需要調(diào)用原生dom api設(shè)置根元素的textContent進(jìn)行簡單的文本更新即可
? ? ? ? ? 我們當(dāng)前并不滿足文本節(jié)點(diǎn)條件巴帮,故進(jìn)入if判斷,由于oldCh 和 ch來源自不同的引用虐秋,故條件為true榕茧,調(diào)用updateChildren方法,入?yún)椋篿d為app的div客给、新舊vnode用押、[]、undefine靶剑。也就是說蜻拨,在此之前池充,vue針對的是template內(nèi)的根元素做的更新,其子元素還未有任何操作? ?
? ??????????oldStartIdx=0缎讼、oldEndIdx=2纵菌、newStartIdx=0、newEndIdx=0休涤、oldStartVnode=組件vnode、oldEndVnode=button按鈕笛辟、newStartVnode=組件vnode功氨、newEndVnode=button按鈕
? ??????????調(diào)用checkDuplicateKeys,判斷key是否重復(fù)手幢,因?yàn)榈湫偷膗l+li結(jié)構(gòu)下vue是要求我們給定不同的key的捷凄,因?yàn)関ue在更新階段會根據(jù)key進(jìn)行更新走不同的邏輯,因?yàn)檫@里是在確保key是唯一的
????????????? 執(zhí)行while循環(huán)围来,這實(shí)際上就是我們常提起的高大上的diff算法的核心
? ??????????????????符合判斷跺涤,進(jìn)入while循環(huán)。走到sameVnode判斷中监透,再次調(diào)用patchVnode桶错,入?yún)椋盒屡f組件vnode、【】胀蛮、包含三個(gè)子元素的組件vnode院刁、0,重復(fù)之前的邏輯,在本次的patch流程中將進(jìn)入之前跳過的step
? ??????????????????????step1
? ? ? ? ? ? ? ? ? ? ? ? ? ? --調(diào)用prepatch粪狼,入?yún)樾屡f組件vnode
? ? ? ? ? ? ? ? ? ? ? ? ? ? --調(diào)用updateChildComponent退腥,入?yún)樯弦淮?點(diǎn)擊按鈕前組件render過程中生成的vnode,是實(shí)際的渲染vnode)再榄、props(flag:true)狡刘、定義的事件、組件vnode困鸥、undefined嗅蔬。在該函數(shù)中將對一些值進(jìn)行訪問和更新,以觸發(fā)其set觸發(fā)notify更新窝革,因此將執(zhí)行子組件的patch過程
? ??????????回到while循環(huán)购城,這次拿到的是中間的文本節(jié)點(diǎn),再次進(jìn)入patchvnode虐译,此次將執(zhí)行到nodeOps.setTextContent(elm,?vnode.text)更新text文本節(jié)點(diǎn)
? ??????????再次回到while循環(huán)瘪板,本次拿到的是button按鈕,由于button沒有改變漆诽,因此實(shí)際上是沒有被更新的
? ? 當(dāng)三次更新完畢后侮攀,則app.vue更新流程結(jié)束