這篇文章主要介紹Vue2.x中虛擬DOM diff原理的示例分析,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!
站在用戶的角度思考問題,與客戶深入溝通,找到德城網(wǎng)站設(shè)計與德城網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:網(wǎng)站設(shè)計制作、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、空間域名、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋德城地區(qū)。前言
經(jīng)??吹街v解Vue2的虛擬Dom diff原理的,但很多都是在原代碼的基礎(chǔ)上添加些注釋等等,這里從0行代碼開始實現(xiàn)一個Vue2的虛擬DOM
實現(xiàn)VNode
src/core/vdom/Vnode.js
export class VNode{ constructor ( tag, //標(biāo)簽名 children,//孩子[VNode,VNode], text, //文本節(jié)點(diǎn) elm //對應(yīng)的真實dom對象 ){ this.tag = tag; this.children = children this.text = text; this.elm = elm; } } export function createTextNode(val){ //為什么這里默認(rèn)把elm置為undefined,不直接根據(jù)tag 用document.createElement(tagName)把elm賦值?而要等后面createElm時候再賦值呢? return new VNode(undefined,undefined,String(val),undefined) } export function createCommentNode(tag,children){ if(children){ for(var i=0;i定義一個Vnode類, 創(chuàng)建節(jié)點(diǎn)分為兩類,一類為text節(jié)點(diǎn),一類非text節(jié)點(diǎn)
src/main.js
import {VNode,createCommentNode} from './core/vdom/vnode' var newVonde = createCommentNode('ul',[createCommentNode('li',['item 1']),createCommentNode('li',['item 2']),createCommentNode('li',['item 3'])])在main.js就可以根據(jù)Vnode 生成對應(yīng)的Vnode對象,上述代碼對應(yīng)的dom表示
- item1
- item2
- item3
先實現(xiàn)不用diff把Vnode渲染到頁面中來
為什么先來實現(xiàn)不用diff渲染Vnode的部分,這里也是為了統(tǒng)計渲染的時間,來表明一個道理。并不是diff就比非diff要開,虛擬DOM并不是任何時候性能都比非虛擬DOM 要快
先來實現(xiàn)一個工具函數(shù),不熟悉的人可以手工敲下代碼 熟悉下
// 真實的dom操作 // src/core/vdom/node-ops.js export function createElement (tagName) { return document.createElement(tagName) } export function createTextNode (text) { return document.createTextNode(text) } export function createComment (text) { return document.createComment(text) } export function insertBefore (parentNode, newNode, referenceNode) { parentNode.insertBefore(newNode, referenceNode) } export function removeChild (node, child) { node.removeChild(child) } export function appendChild (node, child) { node.appendChild(child) } export function parentNode (node) { return node.parentNode } export function nextSibling (node) { return node.nextSibling } export function tagName (node) { return node.tagName } export function setTextContent (node, text) { node.textContent = text } export function setAttribute (node, key, val) { node.setAttribute(key, val) }
src/main.js
import {VNode,createCommentNode} from './core/vdom/vnode' import patch from './core/vdom/patch' var container = document.getElementById("app"); var oldVnode = new VNode(container.tagName,[],undefined,container); var newVonde = createCommentNode('ul',[createCommentNode('li',['item 1']),createCommentNode('li',['item 2']),createCommentNode('li',['item 3'])]) console.time('start'); patch(oldVnode,newVonde); //渲染頁面 console.timeEnd('start');
這里我們要實現(xiàn)一個patch方法,把Vnode渲染到頁面中
src/core/vdom/patch.js
import * as nodeOps from './node-ops' import VNode from './vnode' export default function patch(oldVnode,vnode){ let isInitialPatch = false; if(sameVnode(oldVnode,vnode)){ //如果兩個Vnode節(jié)點(diǎn)的根一致 開始diff patchVnode(oldVnode,vnode) }else{ //這里就是不借助diff的實現(xiàn) const oldElm = oldVnode.elm; const parentElm = nodeOps.parentNode(oldElm); createElm( vnode, parentElm, nodeOps.nextSibling(oldElm) ) if(parentElm != null){ removeVnodes(parentElm,[oldVnode],0,0) } } return vnode.elm; } function patchVnode(oldVnode,vnode,removeOnly){ if(oldVnode === vnode){ return } const elm = vnode.elm = oldVnode.elm const oldCh = oldVnode.children; const ch = vnode.children if(isUndef(vnode.text)){ //非文本節(jié)點(diǎn) if(isDef(oldCh) && isDef(ch)){ //都有字節(jié)點(diǎn) if(oldCh !== ch){ //更新children updateChildren(elm,oldCh,ch,removeOnly); } }else if(isDef(ch)){ //新的有子節(jié)點(diǎn),老的沒有 if(isDef(oldVnode.text)){ nodeOps.setTextContent(elm,''); } //添加子節(jié)點(diǎn) addVnodes(elm,null,ch,0,ch.length-1) }else if(isDef(oldCh)){ //老的有子節(jié)點(diǎn),新的沒有 removeVnodes(elm,oldCh,0,oldCh.length-1) }else if(isDef(oldVnode.text)){ //否則老的有文本內(nèi)容 直接置空就行 nodeOps.setTextContent(elm,''); } }else if(oldVnode.text !== vnode.text){ //直接修改文本 nodeOps.setTextContent(elm,vnode.text); } } function updateChildren(parentElm,oldCh,newCh,removeOnly){ //這里認(rèn)真讀下,沒什么難度的,不行的話 也可以搜索下圖文描述這段過程的 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 refElm; const canMove = !removeOnly while(oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx){ if(isUndef(oldStartVnode)){ oldStartVnode = oldCh[++oldStartIdx] }else if(isUndef(oldEndVnode)){ oldEndVnode = oldCh[--oldEndIdx] }else if(sameVnode(oldStartVnode,newStartVnode)){ patchVnode(oldStartVnode,newStartVnode) oldStartVnode = oldCh[++oldStartIdx] newStartVnode = newCh[++newStartIdx] }else if(sameVnode(oldEndVnode,newEndVnode)){ patchVnode(oldEndVnode,newEndVnode) oldEndVnode = oldCh[--oldEndIdx]; newEndVnode = newCh[--newEndIdx]; }else if(sameVnode(oldStartVnode,newEndVnode)){ patchVnode(oldStartVnode,newEndVnode); //更換順序 canMove && nodeOps.insertBefore(parentElm,oldStartVnode.elm,nodeOps.nextSibling(oldEndVnode.elm)) oldStartVnode = oldCh[++oldStartIdx] newEndVnode = newCh[--newEndIdx] }else if(sameVnode(oldEndVnode,newStartVnode)){ patchVnode(oldEndVnode,newStartVnode) canMove && nodeOps.insertBefore(parentElm,oldEndVnode.elm,oldStartVnode.elm) oldEndVnode = oldCh[--oldEndIdx] newStartVnode = newCh[++newStartIdx] }else{ createElm(newStartVnode,parentElm,oldStartVnode.elm) newStartVnode = newCh[++newStartIdx]; } } if(oldStartIdx > oldEndIdx){ //老的提前相遇,添加新節(jié)點(diǎn)中沒有比較的節(jié)點(diǎn) refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx+1].elm addVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx) }else{ //新的提前相遇 刪除多余的節(jié)點(diǎn) removeVnodes(parentElm,oldCh,oldStartIdx,oldEndIdx) } } function removeVnodes(parentElm,vnodes,startIdx,endIdx){ for(;startIdx<=endIdx;++startIdx){ const ch = vnodes[startIdx]; if(isDef(ch)){ removeNode(ch.elm) } } } function addVnodes(parentElm,refElm,vnodes,startIdx,endIdx){ for(;startIdx <=endIdx;++startIdx ){ createElm(vnodes[startIdx],parentElm,refElm) } } function sameVnode(vnode1,vnode2){ return vnode1.tag === vnode2.tag } function removeNode(el){ const parent = nodeOps.parentNode(el) if(parent){ nodeOps.removeChild(parent,el) } } function removeVnodes(parentElm,vnodes,startIdx,endIdx){ for(;startIdx<=endIdx;++startIdx){ const ch = vnodes[startIdx] if(isDef(ch)){ removeNode(ch.elm) } } } function isDef (s){ return s != null } function isUndef(s){ return s == null } function createChildren(vnode,children){ if(Array.isArray(children)){ for(let i=0;i這就是完整實現(xiàn)了
以上是“Vue2.x中虛擬DOM diff原理的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計公司行業(yè)資訊頻道!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
標(biāo)題名稱:Vue2.x中虛擬DOMdiff原理的示例分析-創(chuàng)新互聯(lián)
文章位置:http://weahome.cn/article/gpicj.html