十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
這篇文章主要介紹Vue2.x中虛擬DOM diff原理的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

成都一家集口碑和實力的網(wǎng)站建設(shè)服務(wù)商,擁有專業(yè)的企業(yè)建站團隊和靠譜的建站技術(shù),10余年企業(yè)及個人網(wǎng)站建設(shè)經(jīng)驗 ,為成都1000+客戶提供網(wǎng)頁設(shè)計制作,網(wǎng)站開發(fā),企業(yè)網(wǎng)站制作建設(shè)等服務(wù),包括成都營銷型網(wǎng)站建設(shè),品牌網(wǎng)站制作,同時也為不同行業(yè)的客戶提供成都網(wǎng)站建設(shè)、網(wǎng)站制作的服務(wù),包括成都電商型網(wǎng)站制作建設(shè),裝修行業(yè)網(wǎng)站制作建設(shè),傳統(tǒng)機械行業(yè)網(wǎng)站建設(shè),傳統(tǒng)農(nóng)業(yè)行業(yè)網(wǎng)站制作建設(shè)。在成都做網(wǎng)站,選網(wǎng)站制作建設(shè)服務(wù)商就選創(chuàng)新互聯(lián)。
前言
經(jīng)常看到講解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é)點
elm //對應(yīng)的真實dom對象
){
this.tag = tag;
this.children = children
this.text = text;
this.elm = elm;
}
}
export function createTextNode(val){
//為什么這里默認把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é)點分為兩類,一類為text節(jié)點,一類非text節(jié)點
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é)點的根一致 開始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é)點
if(isDef(oldCh) && isDef(ch)){
//都有字節(jié)點
if(oldCh !== ch){
//更新children
updateChildren(elm,oldCh,ch,removeOnly);
}
}else if(isDef(ch)){
//新的有子節(jié)點,老的沒有
if(isDef(oldVnode.text)){
nodeOps.setTextContent(elm,'');
}
//添加子節(jié)點
addVnodes(elm,null,ch,0,ch.length-1)
}else if(isDef(oldCh)){
//老的有子節(jié)點,新的沒有
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){
//這里認真讀下,沒什么難度的,不行的話 也可以搜索下圖文描述這段過程的
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é)點中沒有比較的節(jié)點
refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx+1].elm
addVnodes(parentElm,refElm,newCh,newStartIdx,newEndIdx)
}else{
//新的提前相遇 刪除多余的節(jié)點
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)行業(yè)資訊頻道!
本文題目:Vue2.x中虛擬DOMdiff原理的示例分析
網(wǎng)頁URL:http://www.jiaotiyi.com/article/pjissp.html