<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

        vue 虛擬dom的patch源碼分析

        來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 22:18:14
        文檔

        vue 虛擬dom的patch源碼分析

        vue 虛擬dom的patch源碼分析:本文介紹了vue 虛擬dom的patch源碼分析,分享給大家,具體如下: 源碼目錄:src/core/vdom/patch.js function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartId
        推薦度:
        導(dǎo)讀vue 虛擬dom的patch源碼分析:本文介紹了vue 虛擬dom的patch源碼分析,分享給大家,具體如下: 源碼目錄:src/core/vdom/patch.js function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartId

        本文介紹了vue 虛擬dom的patch源碼分析,分享給大家,具體如下:

        源碼目錄:src/core/vdom/patch.js

         function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
         let oldStartIdx = 0
         let newStartIdx = 0
         let oldEndIdx = oldCh.length - 1
         let oldStartVnode = oldCh[0]
         let oldEndVnode = oldCh[oldEndIdx]
         let newEndIdx = newCh.length - 1
         let newStartVnode = newCh[0]
         let newEndVnode = newCh[newEndIdx]
         let oldKeyToIdx, idxInOld, vnodeToMove, refElm
        
         const canMove = !removeOnly
        
         while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { // 開始索引大于結(jié)束索引,進(jìn)不了
         if (isUndef(oldStartVnode)) {
         oldStartVnode = oldCh[++oldStartIdx] // Vnode已經(jīng)被移走了。
         } else if (isUndef(oldEndVnode)) {
         oldEndVnode = oldCh[--oldEndIdx]
         } else if (sameVnode(oldStartVnode, newStartVnode)) {
         patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
         oldStartVnode = oldCh[++oldStartIdx] // 索引加1。是去對比下一個節(jié)點(diǎn)。比如之前start=a[0],那現(xiàn)在start=a[1],改變start的值后再去對比start這個vnode
         newStartVnode = newCh[++newStartIdx]
         
         } else if (sameVnode(oldEndVnode, newEndVnode)) { 
         patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
         oldEndVnode = oldCh[--oldEndIdx]
         newEndVnode = newCh[--newEndIdx]
         } else if (sameVnode(oldStartVnode, newEndVnode)) { 
         patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
         canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))// 把節(jié)點(diǎn)b移到樹的最右邊
         oldStartVnode = oldCh[++oldStartIdx]
         newEndVnode = newCh[--newEndIdx]
         
         } else if (sameVnode(oldEndVnode, newStartVnode)) { old.end.d=new.start.d
         patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
         canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)// Vnode moved left,把d移到c的左邊。=old.start->old.end
         oldEndVnode = oldCh[--oldEndIdx] 
         newStartVnode = newCh[++newStartIdx] 
        
         } else {
         if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
         idxInOld = isDef(newStartVnode.key)
         ? oldKeyToIdx[newStartVnode.key]
         : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
         if (isUndef(idxInOld)) { 
         createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) // 創(chuàng)建新節(jié)點(diǎn),后面執(zhí)行了nodeOps.insertBefore(parent, elm, ref)
         } else {
         vnodeToMove = oldCh[idxInOld]
         /* istanbul ignore if */
         if (process.env.NODE_ENV !== 'production' && !vnodeToMove) {
         warn(
         'It seems there are duplicate keys that is causing an update error. ' +
         'Make sure each v-for item has a unique key.'
         )
         }
         if (sameVnode(vnodeToMove, newStartVnode)) {
         patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
         oldCh[idxInOld] = undefined
         canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
         } else {
         // same key but different element. treat as new element
         createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
         }
         }
         newStartVnode = newCh[++newStartIdx] 
         
         }
         }
         if (oldStartIdx > oldEndIdx) {
         refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm
         addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)
         } else if (newStartIdx > newEndIdx) {
         removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) // 刪除舊的c,removeNode(ch.elm)
        
         }
         }
        function sameVnode (a, b) {
         return (
         a.key === b.key && (
         (
         a.tag === b.tag &&
         a.isComment === b.isComment &&
         isDef(a.data) === isDef(b.data) &&
         sameInputType(a, b)
         ) || (
         isTrue(a.isAsyncPlaceholder) &&
         a.asyncFactory === b.asyncFactory &&
         isUndef(b.asyncFactory.error)
         )
         )
         )
        }
        
        /**
         * 比較新舊vnode節(jié)點(diǎn),根據(jù)不同的狀態(tài)對dom做合理的更新操作(添加,移動,刪除)整個過程還會依次調(diào)用prepatch,update,postpatch等鉤子函數(shù),在編譯階段生成的一些靜態(tài)子樹,在這個過程
         * @param oldVnode 中由于不會改變而直接跳過比對,動態(tài)子樹在比較過程中比較核心的部分就是當(dāng)新舊vnode同時存在children,通過updateChildren方法對子節(jié)點(diǎn)做更新,
         * @param vnode
         * @param insertedVnodeQueue
         * @param removeOnly
         */
         function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
         if (oldVnode === vnode) {
         return
         }
        
         const elm = vnode.elm = oldVnode.elm
        
         if (isTrue(oldVnode.isAsyncPlaceholder)) {
         if (isDef(vnode.asyncFactory.resolved)) {
         hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
         } else {
         vnode.isAsyncPlaceholder = true
         }
         return
         }
        
         // 用于靜態(tài)樹的重用元素。
         // 注意,如果vnode是克隆的,我們只做這個。
         // 如果新節(jié)點(diǎn)不是克隆的,則表示呈現(xiàn)函數(shù)。
         // 由熱重加載api重新設(shè)置,我們需要進(jìn)行適當(dāng)?shù)闹匦落秩尽? if (isTrue(vnode.isStatic) &&
         isTrue(oldVnode.isStatic) &&
         vnode.key === oldVnode.key &&
         (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
         ) {
         vnode.componentInstance = oldVnode.componentInstance
         return
         }
        
         let i
         const data = vnode.data
         if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
         i(oldVnode, vnode)
         }
        
         const oldCh = oldVnode.children
         const ch = vnode.children
         if (isDef(data) && isPatchable(vnode)) {
         for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)
         if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)
         }
         if (isUndef(vnode.text)) {
         if (isDef(oldCh) && isDef(ch)) {
         if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)
         } else if (isDef(ch)) {
         if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')
         addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)
         } else if (isDef(oldCh)) {
         removeVnodes(elm, oldCh, 0, oldCh.length - 1)
         } else if (isDef(oldVnode.text)) {
         nodeOps.setTextContent(elm, '')
         }
         } else if (oldVnode.text !== vnode.text) {
         nodeOps.setTextContent(elm, vnode.text)
         }
         if (isDef(data)) {
         if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)
         }
         }
        
        function insertBefore (parentNode, newNode, referenceNode) {
         parentNode.insertBefore(newNode, referenceNode);
        }
        
        /**
         *
         * @param vnode根據(jù)vnode的數(shù)據(jù)結(jié)構(gòu)創(chuàng)建真實(shí)的dom節(jié)點(diǎn),如果vnode有children則會遍歷這些子節(jié)點(diǎn),遞歸調(diào)用createElm方法,
         * @param insertedVnodeQueue記錄子節(jié)點(diǎn)創(chuàng)建順序的隊(duì)列,每創(chuàng)建一個dom元素就會往隊(duì)列中插入當(dāng)前的vnode,當(dāng)整個vnode對象全部轉(zhuǎn)換成為真實(shí)的dom 樹時,會依次調(diào)用這個隊(duì)列中vnode hook的insert方法
         * @param parentElm
         * @param refElm
         * @param nested
         */
        
         let inPre = 0
         function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {
         vnode.isRootInsert = !nested // 過渡進(jìn)入檢查
         if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {
         return
         }
        
         const data = vnode.data
         const children = vnode.children
         const tag = vnode.tag
         if (isDef(tag)) {
         if (process.env.NODE_ENV !== 'production') {
         if (data && data.pre) {
         inPre++
         }
         if (
         !inPre &&
         !vnode.ns &&
         !(
         config.ignoredElements.length &&
         config.ignoredElements.some(ignore => {
         return isRegExp(ignore)
         ? ignore.test(tag)
         : ignore === tag
         })
         ) &&
         config.isUnknownElement(tag)
         ) {
         warn(
         'Unknown custom element: <' + tag + '> - did you ' +
         'register the component correctly? For recursive components, ' +
         'make sure to provide the "name" option.',
         vnode.context
         )
         }
         }
         vnode.elm = vnode.ns
         ? nodeOps.createElementNS(vnode.ns, tag)
         : nodeOps.createElement(tag, vnode)
         setScope(vnode)
        
         /* istanbul ignore if */
         if (__WEEX__) {
         // in Weex, the default insertion order is parent-first.
         // List items can be optimized to use children-first insertion
         // with append="tree".
         const appendAsTree = isDef(data) && isTrue(data.appendAsTree)
         if (!appendAsTree) {
         if (isDef(data)) {
         invokeCreateHooks(vnode, insertedVnodeQueue)
         }
         insert(parentElm, vnode.elm, refElm)
         }
         createChildren(vnode, children, insertedVnodeQueue)
         if (appendAsTree) {
         if (isDef(data)) {
         invokeCreateHooks(vnode, insertedVnodeQueue)
         }
         insert(parentElm, vnode.elm, refElm)
         }
         } else {
         createChildren(vnode, children, insertedVnodeQueue)
         if (isDef(data)) {
         invokeCreateHooks(vnode, insertedVnodeQueue)
         }
         insert(parentElm, vnode.elm, refElm)
         }
        
         if (process.env.NODE_ENV !== 'production' && data && data.pre) {
         inPre--
         }
         } else if (isTrue(vnode.isComment)) {
         vnode.elm = nodeOps.createComment(vnode.text)
         insert(parentElm, vnode.elm, refElm)
         } else {
         vnode.elm = nodeOps.createTextNode(vnode.text)
         insert(parentElm, vnode.elm, refElm)
         }
         }
        function insert (parent, elm, ref) {
         if (isDef(parent)) {
         if (isDef(ref)) {
         if (ref.parentNode === parent) {
         nodeOps.insertBefore(parent, elm, ref)
         }
         } else {
         nodeOps.appendChild(parent, elm)
         }
         }
         }
        
        function removeVnodes (parentElm, vnodes, startIdx, endIdx) {
         for (; startIdx <= endIdx; ++startIdx) {
         const ch = vnodes[startIdx]
         if (isDef(ch)) {
         if (isDef(ch.tag)) {
         removeAndInvokeRemoveHook(ch)
         invokeDestroyHook(ch)
         } else { // Text node
         removeNode(ch.elm)
         }
         }
         }
         }

        updateChildren方法主要通過while循環(huán)去對比2棵樹的子節(jié)點(diǎn)來更新dom,通過對比新的來改變舊的,以達(dá)到新舊統(tǒng)一的目的。

        通過一個例子來模擬一下:

        假設(shè)有新舊2棵樹,樹中的子節(jié)點(diǎn)分別為a,b,c,d等表示,不同的代號代表不同的vnode,如:

        在設(shè)置好狀態(tài)后,我們開始第一遍比較,此時oldStartVnode=a,newStartVnode=a;命中了sameVnode(oldStartVnode,newStartVnode)邏輯,則直接調(diào)用patchVnode(oldStartVnode,newStartVnode,insertedVnodeQueue)方法更新節(jié)點(diǎn)a,接著把oldStartIdxnewStartIdx索引分別+1,如圖:

        更新完節(jié)點(diǎn)a后,我們開始第2遍比較,此時oldStartVnode=b,newEndVnode=b;命中了sameVnode(oldStartVnode,newEndVnode)邏輯,則調(diào)用patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)方法更新節(jié)點(diǎn)b,接著調(diào)用canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm)),把節(jié)點(diǎn)b移到樹的最右邊,最后把oldStartIdx索引+1,newEndIdx索引-1,如圖:

        更新完節(jié)點(diǎn)b后,我們開始第三遍比較,此時oldEndVnode=d,newStartVnode=d;命中了sameVnode(oldEndVnode, newStartVnode)邏輯,則調(diào)用patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)方法更新節(jié)點(diǎn)d,接著調(diào)用canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm),把d移到c的左邊。最后把oldEndIdx索引-1,newStartIdx索引+1,如圖:

        更新完d后,我們開始第4遍比較,此時newStartVnode=e,節(jié)點(diǎn)e在舊樹里是沒有的,因此應(yīng)該被作為一個新的元素插入,調(diào)用createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm),后面執(zhí)行了nodeOps.insertBefore(parent, elm, ref)方法把e插入到c之前,接著把newStartIdx索引+1,如圖:

        插入節(jié)點(diǎn)e后,我們可以看到newStartIdx已經(jīng)大于newEndIdx了,while循環(huán)已經(jīng)完畢。接著調(diào)用removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx) 刪除舊的c,最終如圖:

        updateChildren通過以上幾步操作完成了舊樹子節(jié)點(diǎn)的更新,實(shí)際上只用了比較小的dom操作,在性能上有所提升,并且當(dāng)子節(jié)點(diǎn)越復(fù)雜,這種提升效果越明顯。vnode通過patch方法生成dom后,會調(diào)用mounted hook,至此,整個vue實(shí)例就創(chuàng)建完成了,當(dāng)這個vue實(shí)例的watcher觀察到數(shù)據(jù)變化時,會兩次調(diào)用render方法生成新的vnode,接著調(diào)用patch方法對比新舊vnode來更新dom.

        聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        vue 虛擬dom的patch源碼分析

        vue 虛擬dom的patch源碼分析:本文介紹了vue 虛擬dom的patch源碼分析,分享給大家,具體如下: 源碼目錄:src/core/vdom/patch.js function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartId
        推薦度:
        標(biāo)簽: VUE 源碼 vu
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 久久性生大片免费观看性| 亚洲国产日韩a在线播放| 久久精品无码免费不卡| 亚洲日本一区二区三区在线不卡| 亚洲av中文无码乱人伦在线观看| 四虎在线视频免费观看| 亚洲av无码一区二区三区在线播放 | 亚洲国产成人无码AV在线影院| 免费精品国产日韩热久久| 77777午夜亚洲| 在线观看免费污视频| 国产精品自拍亚洲| 亚洲午夜精品一级在线播放放| 天堂在线免费观看| 亚洲成aⅴ人在线观看| 妞干网免费视频观看| 羞羞漫画登录页面免费| 亚洲中文字幕无码不卡电影| 免费视频精品一区二区三区| 亚洲国产成人在线视频| 国产美女无遮挡免费网站| 亚欧洲精品在线视频免费观看| 亚洲成A人片在线观看无码不卡| 8x8×在线永久免费视频| 亚洲成_人网站图片| 亚洲一级片免费看| 中文字幕无码播放免费| 亚洲AV无码一区二区三区牲色| 伊人婷婷综合缴情亚洲五月| 69视频在线观看高清免费| 亚洲中文字幕一二三四区| 国产精品亚洲产品一区二区三区| 99久久精品免费精品国产| 亚洲日韩AV一区二区三区中文| 久久精品夜色噜噜亚洲A∨| 1000部禁片黄的免费看| 成人免费夜片在线观看| 91精品国产亚洲爽啪在线影院| 日本无吗免费一二区| 国产精品免费观看调教网| 国产精品自拍亚洲|