在vue中实现虚拟dom的patch(详细教程)
这篇文章主要介绍了vue 虚拟dom的patch源码分析,现在分享给大家,也给大家做个参考。
本文介绍了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) { // 开始索引大于结束索引,进不了 if (isUndef(oldStartVnode)) { oldStartVnode = oldCh[++oldStartIdx] // Vnode已经被移走了。 } else if (isUndef(oldEndVnode)) { oldEndVnode = oldCh[--oldEndIdx] } else if (sameVnode(oldStartVnode, newStartVnode)) { patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue) oldStartVnode = oldCh[++oldStartIdx] // 索引加1。是去对比下一个节点。比如之前start=a[0],那现在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))// 把节点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) // 创建新节点,后面执行了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节点,根据不同的状态对dom做合理的更新操作(添加,移动,删除)整个过程还会依次调用prepatch,update,postpatch等钩子函数,在编译阶段生成的一些静态子树,在这个过程 * @param oldVnode 中由于不会改变而直接跳过比对,动态子树在比较过程中比较核心的部分就是当新旧vnode同时存在children,通过updateChildren方法对子节点做更新, * @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 } // 用于静态树的重用元素。 // 注意,如果vnode是克隆的,我们只做这个。 // 如果新节点不是克隆的,则表示呈现函数。 // 由热重加载api重新设置,我们需要进行适当的重新渲染。 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根据vnode的数据结构创建真实的dom节点,如果vnode有children则会遍历这些子节点,递归调用createElm方法, * @param insertedVnodeQueue记录子节点创建顺序的队列,每创建一个dom元素就会往队列中插入当前的vnode,当整个vnode对象全部转换成为真实的dom 树时,会依次调用这个队列中vnode hook的insert方法 * @param parentElm * @param refElm * @param nested */ let inPre = 0 function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { vnode.isRootInsert = !nested // 过渡进入检查 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
循环去对比2棵树的子节点来更新dom
,通过对比新的来改变旧的,以达到新旧统一的目的。
通过一个例子来模拟一下:
假设有新旧2棵树,树中的子节点分别为a,b,c,d
等表示,不同的代号代表不同的vnode
,如:
在设置好状态后,我们开始第一遍比较,此时oldStartVnode=a,newStartVnode=a;
命中了sameVnode(oldStartVnode,newStartVnode)
逻辑,则直接调用patchVnode(oldStartVnode,newStartVnode,insertedVnodeQueue)
方法更新节点a
,接着把oldStartIdx
和newStartIdx
索引分别+1,如图:
更新完节点a
后,我们开始第2遍比较,此时oldStartVnode=b,newEndVnode=b;
命中了sameVnode(oldStartVnode,newEndVnode)
逻辑,则调用patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
方法更新节点b
,接着调用canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
,把节点b
移到树的最右边,最后把oldStartIdx
索引+1,newEndIdx
索引-1,如图:
更新完节点b
后,我们开始第三遍比较,此时oldEndVnode=d,newStartVnode=d;
命中了sameVnode(oldEndVnode, newStartVnode)
逻辑,则调用patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
方法更新节点d
,接着调用canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
,把d
移到c
的左边。最后把oldEndIdx
索引-1,newStartIdx
索引+1,如图:
更新完d
后,我们开始第4遍比较,此时newStartVnode=e
,节点e
在旧树里是没有的,因此应该被作为一个新的元素插入,调用createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
,后面执行了nodeOps.insertBefore(parent, elm, ref)
方法把e
插入到c
之前,接着把newStartIdx
索引+1,如图:
插入节点e
后,我们可以看到newStartIdx
已经大于newEndIdx
了,while
循环已经完毕。接着调用removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)
删除旧的c
,最终如图:
updateChildren
通过以上几步操作完成了旧树子节点的更新,实际上只用了比较小的dom
操作,在性能上有所提升,并且当子节点越复杂,这种提升效果越明显。vnode
通过patch
方法生成dom
后,会调用mounted hook
,至此,整个vue
实例就创建完成了,当这个vue
实例的watcher
观察到数据变化时,会两次调用render
方法生成新的vnode
,接着调用patch
方法对比新旧vnode
来更新dom
.
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
Atas ialah kandungan terperinci 在vue中实现虚拟dom的patch(详细教程). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

AI Hentai Generator
Menjana ai hentai secara percuma.

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Dewu APP pada masa ini merupakan perisian beli-belah jenama yang sangat popular, tetapi kebanyakan pengguna tidak tahu cara menggunakan fungsi dalam APP Dewu Panduan tutorial penggunaan yang paling terperinci Seterusnya, editor membawakan Dewuduo kepada pengguna tutorial. Pengguna yang berminat boleh datang dan lihat! Tutorial cara menggunakan Dewu [2024-03-20] Cara menggunakan pembelian ansuran Dewu [2024-03-20] Cara mendapatkan kupon Dewu [2024-03-20] Cara mencari perkhidmatan pelanggan manual Dewu [2024-03- 20] Cara menyemak kod pikap Dewu【2024-03-20】Di mana hendak mencari pembelian Dewu【2024-03-20】Cara membuka VIP Dewu【2024-03-20】Cara memohon pemulangan atau pertukaran Dewi

Pelayar Quark ialah pelayar berbilang fungsi yang sangat popular pada masa ini, tetapi kebanyakan rakan tidak tahu cara menggunakan fungsi dalam Pelayar Quark Fungsi dan teknik yang paling biasa digunakan akan disusun di bawah . Berikut ialah ringkasan tutorial penggunaan berbilang fungsi Pelayar Quark. Pengguna yang berminat boleh datang dan melihat bersama-sama! Tutorial penggunaan Penyemak Imbas Quark [2024-01-09]: Cara mengimbas kertas ujian untuk melihat jawapan dalam Quark [2024-01-09]: Bagaimana untuk mendayakan mod dewasa dalam Pelayar Quark [2024-01-09]: Bagaimana untuk padamkan ruang terpakai Quark [2024 -01-09]: Cara membersihkan ruang storan cakera rangkaian Quark [2024-01-09]: Cara membatalkan sandaran Quark [2024-01-09]: Quark

Cara menaik taraf versi numpy: Tutorial yang mudah diikuti, memerlukan contoh kod konkrit Pengenalan: NumPy ialah perpustakaan Python penting yang digunakan untuk pengkomputeran saintifik. Ia menyediakan objek tatasusunan berbilang dimensi yang berkuasa dan satu siri fungsi berkaitan yang boleh digunakan untuk melaksanakan operasi berangka yang cekap. Apabila versi baharu dikeluarkan, ciri yang lebih baharu dan pembetulan pepijat sentiasa tersedia kepada kami. Artikel ini akan menerangkan cara untuk menaik taraf pustaka NumPy anda yang dipasang untuk mendapatkan ciri terkini dan menyelesaikan isu yang diketahui. Langkah 1: Semak versi NumPy semasa pada permulaan

Selepas hujan pada musim panas, anda sering dapat melihat pemandangan cuaca istimewa yang indah dan ajaib - pelangi. Ini juga merupakan pemandangan jarang yang boleh ditemui dalam fotografi, dan ia sangat fotogenik. Terdapat beberapa syarat untuk pelangi muncul: pertama, terdapat titisan air yang mencukupi di udara, dan kedua, matahari bersinar pada sudut yang lebih rendah. Oleh itu, adalah paling mudah untuk melihat pelangi pada sebelah petang selepas hujan reda. Walau bagaimanapun, pembentukan pelangi sangat dipengaruhi oleh cuaca, cahaya dan keadaan lain, jadi ia biasanya hanya bertahan untuk jangka masa yang singkat, dan masa tontonan dan penangkapan terbaik adalah lebih pendek. Jadi apabila anda menemui pelangi, bagaimanakah anda boleh merakamnya dengan betul dan mengambil gambar dengan kualiti? 1. Cari pelangi Selain keadaan yang dinyatakan di atas, pelangi biasanya muncul mengikut arah cahaya matahari, iaitu jika matahari bersinar dari barat ke timur, pelangi lebih cenderung muncul di timur.

1. Mula-mula buka WeChat. 2. Klik [+] di penjuru kanan sebelah atas. 3. Klik kod QR untuk mengutip bayaran. 4. Klik tiga titik kecil di penjuru kanan sebelah atas. 5. Klik untuk menutup peringatan suara untuk ketibaan pembayaran.

Menguji monitor semasa membelinya adalah bahagian penting untuk mengelakkan membeli yang rosak Hari ini saya akan mengajar anda cara menggunakan perisian untuk menguji monitor. Langkah kaedah 1. Mula-mula, cari dan muat turun perisian DisplayX di tapak web ini, pasang dan bukanya, dan anda akan melihat banyak kaedah pengesanan yang diberikan kepada pengguna. 2. Pengguna mengklik pada ujian lengkap biasa Langkah pertama adalah untuk menguji kecerahan paparan Pengguna melaraskan paparan supaya kotak dapat dilihat dengan jelas. 3. Kemudian klik tetikus untuk masuk ke pautan seterusnya Jika monitor boleh membezakan setiap kawasan hitam dan putih, bermakna monitor masih elok. 4. Klik butang kiri tetikus sekali lagi, dan anda akan melihat ujian skala kelabu monitor Lebih lancar peralihan warna, lebih baik monitor. 5. Selain itu, dalam perisian displayx kita

PhotoshopCS ialah singkatan daripada Photoshop Creative Suite Ia adalah perisian yang dihasilkan oleh Adobe Ia digunakan secara meluas dalam reka bentuk grafik dan pemprosesan imej Sebagai seorang pelajar baru yang belajar PS, hari ini biarkan editor menerangkan kepada anda apa itu perisian photoshopcs5. . 1. Apakah perisian photoshop cs5? Adobe Photoshop CS5 Extended sesuai untuk profesional dalam bidang filem, video dan multimedia, pereka grafik dan web yang menggunakan 3D dan animasi, dan profesional dalam bidang kejuruteraan dan saintifik. Paparkan imej 3D dan cantumkannya menjadi imej komposit 2D. Edit video dengan mudah

Dengan perkembangan telefon pintar yang berterusan, fungsi telefon bimbit semakin berkuasa, antaranya fungsi mengambil gambar panjang menjadi salah satu fungsi penting yang digunakan oleh ramai pengguna dalam kehidupan seharian. Tangkapan skrin panjang boleh membantu pengguna menyimpan halaman web yang panjang, rekod perbualan atau gambar pada satu masa untuk memudahkan tontonan dan perkongsian. Di antara banyak jenama telefon bimbit, telefon bimbit Huawei juga merupakan salah satu jenama yang sangat dihormati oleh pengguna, dan fungsinya untuk memotong gambar panjang juga sangat dipuji. Artikel ini akan memperkenalkan anda kepada kaedah yang betul untuk mengambil gambar panjang pada telefon mudah alih Huawei, serta beberapa petua pakar untuk membantu anda menggunakan telefon mudah alih Huawei dengan lebih baik.
