图片配合下面整理笔记一起看。
总结:
1、通过 h 函数创建虚拟节点, 调用 vnode函数 return { sel, data, children, text, elm }
2、patch 根据虚拟节点创建真正的 DOM insertBefore 插入到页面,上树之前还是 虚拟格式,那么调用 createElement创建节点,放在自身的 elm 属性上
最后通过 oldVnode.elm.parentNode.insertBefore(newDom, oldVnode.elm) 插入页面,移除旧的 oldVnode
虚拟节点种 key相同,sel 选择器相同,给虚拟节点 打补丁,对比
其中有虚拟DOM对比,调用 patchVnode(oldVnode, newVnode)
3、patchVnode 中进行虚拟节点对比
第一种: newVnode 为 text 文本,oldVnode 有 文本 || 数组, 直接替换旧的
第二种: newVnode 为数组,oldVnode 为 text || 数组
2.1 如果newVnode为数组,oldVnode为text的情况,将oldVnode种的text删除,插入新创建的节点。
2.2 如果newVnode为数组,oldVnode为数组,精细化对比
调用 updateVnode 函数
4、updateVnode 两个虚拟节点孩子进行对比
注意1: 以旧节点为标杆,对旧节点进行移动,删除,内容替换, 新节点一直 ++
注意2: parentElm.insertBefore 改变的是页面中的元素,当前 newChild、oldChild不受影响
while(newStartIndex <= newEndIndex && oldStartIndex <= oldEndIndex) {
4.1 第一种,新前与旧前对比,只改变旧节点种的虚拟dom
A text:1 A text:2
B text:2 B text:2
C text:3 C text:2
4.2 第二种,新后与旧后对比,只改变旧节点种的虚拟dom
D text:1 C text:2
B text:1 B text:2
A text:1 A text:2
4.3 第三种,新后与旧前对比,将新后匹配到的旧节点移动到旧后之后
patchVnode(oldStartVnode, newEndVnode) // 先替换oldStartVnode种的值
parentElm.insertBefore(oldStartVnode.elm, oldEndVnode.elm.nextSibling) //将oldStartVnode先插入,在移动至旧后下一个节点依次向下
旧前 新前
A text:1 C text:2
B text:1 B text:2
C text:1 A text:2
旧后 新后
B text:2
A text:2
4.4 第四种,旧后与新前对比,将新前匹配到的旧后节点移动到旧前的前面
patchVnode(oldEndVnode, newStartVnode)
parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm) //将oldEndVnode先插入,在移动至旧前前面,依次向前
C text:2
B text:2
旧前 新前
A text:1 C text:2
B text:1 B text:2
C text:1 D text:2
旧后 新后
4.5 第五种,都不满足,将旧节点的 key 跟 下标存下来,移动新节点
let zIndex = mapKey[newStartVnode.key]
if (zIndex === undefined) { // 不存在,新建节点,插入到旧前的前面
parentElm.insertBefore(creatElement(newStartVnode), oldStartVnode.elm)
} else {
let remove = oldChild[zIndex]
// 存在,先替换文本,再将旧的改为 undefined,并移动旧的节点
patchVnode(remove, newStartVnode)
oldChild[zIndex] = undefined
parentElm.insertBefore(remove.elm, oldStartVnode.elm) // 在parentElm中新增一个节点,移动到 oldStartVnode 前面
}
++newStartIndex
}
while循环中发现有一方执行完了,下标不相等,跳出循环
4.6 判断新节点中有剩余的节点,是要新增的
newStartIndex <= newEndIndex
旧前 新前
A text:1 A text:2
B text:1 B text:2
C text:1 C text:2
D text:2
E text:2
旧后 新后
for (let i = newStartIndex; i <= newEndIndex; i ++) {
parentElm.insertBefore(creatElement(newChild[i]), oldChild[oldStartIndex].elm) // 如果没有找到这个元素,就在最后插入
}
4.7 判断旧节点中还有剩余的节点,是需要删除的节点
oldStartIndex <= oldEndIndex
旧前 新前
A text:1 A text:2
B text:1 B text:2
C text:1 C text:2
D text:1
E text:1
旧后 新后
for (let i = oldStartIndex; i <= oldEndIndex; i ++) {
if (oldChild[i] && oldChild[i].elm !== undefined) {
parentElm.removeChild(oldChild[i].elm)
}
}
vnode.js
export default function ( sel, data, children, text, elm ) {
const key = data. key ? data. key : undefined
return {
sel,
data,
children,
text,
key,
elm
}
}
h.js
export default function h ( sel, data, c ) {
if ( arguments. length !== 3 ) {
throw new Error ( '请输入三位有效数字' )
}
if ( ( typeof c) === 'string' ) {
return vnode ( sel, data, undefined , c, undefined )
}
if ( Array. isArray ( c) ) {
const arr = [ ]
for ( let i = 0 ; i < c. length; i ++ ) {
if ( ! ( Object . prototype. toString . call ( c[ i] ) === '[object Object]' && c[ i] . hasOwnProperty ( 'sel' ) ) ) {
throw new Error ( '输入的数组格式有误!!!' )
return
}
arr. push ( c[ i] )
}
console. log ( arr, 'hhhhh--141414------->>>' )
return vnode ( sel, data, arr, undefined , undefined )
}
if ( Object . prototype. toString . call ( c) === '[object Object]' && c. hasOwnProperty ( 'sel' ) ) {
return vnode ( sel, data, [ c] , undefined , undefined )
}
throw new Error ( '输入的格式有误~~' )
}
patch.js
import vnode from './vnode'
import creatElement from './creatElement'
import patchVnode from './patchVnode'
export default function patch ( oldVnode, newVnode ) {
console. log ( oldVnode, newVnode)
if ( ! oldVnode. sel) {
const text = oldVnode. innerText ? oldVnode. innerText : undefined
oldVnode = vnode ( oldVnode. tagName. toLowerCase ( ) , { } , [ ] , text, oldVnode)
}
if ( oldVnode. sel === newVnode. sel && oldVnode. key !== undefined && newVnode. key !== undefined && oldVnode. key === newVnode. key) {
patchVnode ( oldVnode, newVnode)
return
}
const newDom = creatElement ( newVnode)
console. log ( newDom, 'newDom' )
console. log ( oldVnode, 'oldVnode' )
if ( newDom) {
oldVnode. elm. parentNode. insertBefore ( newDom, oldVnode. elm)
}
oldVnode. elm. remove ( oldVnode. elm)
}
patchVnode.js
import updateVnode from './updateVnode'
import creatElement from './creatElement'
export default function patchVnode ( oldVnode, newVnode ) {
if ( newVnode. text && newVnode. text !== undefined ) {
if ( oldVnode. text && oldVnode. text !== undefined && newVnode. text === oldVnode. text) {
return
}
oldVnode. elm. innerText = newVnode. text
} else {
console. log ( 'zzzzzzz' ) ;
if ( ( oldVnode. text || oldVnode. text === undefined ) && ( oldVnode. children === undefined || oldVnode. children. length === 0 ) ) {
oldVnode. elm. innerHTML = ''
for ( let i = 0 ; i < newVnode. children. length; i ++ ) {
const itemDom = creatElement ( newVnode. children[ i] )
oldVnode. elm. appendChild ( itemDom)
}
} else if ( oldVnode. children && oldVnode. children. length > 0 && newVnode. children && newVnode. children. length > 0 ) {
console. log ( '进行最精细化比较oldVnode。elm' , oldVnode. elm, oldVnode, newVnode)
updateVnode ( oldVnode. elm, oldVnode. children, newVnode. children)
}
}
}
creatElement.js
export default function creatElement ( newVnode ) {
const container = document. createElement ( newVnode. sel)
if ( newVnode. text && ( newVnode. children === undefined || newVnode. children. length === 0 ) ) {
container. innerText = newVnode. text
} else if ( newVnode. children && newVnode. children. length > 0 ) {
for ( let i = 0 ; i < newVnode. children. length; i ++ ) {
let liDom = creatElement ( newVnode. children[ i] )
container. appendChild ( liDom)
}
}
newVnode. elm = container
console. log ( 'creatElement------>>>>newVnode' , newVnode)
return newVnode. elm
}
updateVnode.js
import patchVnode from "./patchVnode"
import creatElement from "./creatElement"
function checkoutSameVnode ( oldVnode, newVnode ) {
return oldVnode. sel === newVnode. sel && oldVnode. key === newVnode. key
}
export default function updateVnode ( parentElm, oldChild, newChild ) {
debugger
let newStartIndex = 0
let oldStartIndex = 0
let newEndIndex = newChild. length - 1
let oldEndIndex = oldChild. length - 1
let newStartVnode = newChild[ 0 ]
let newEndVnode = newChild[ newEndIndex]
let oldStartVnode = oldChild[ 0 ]
let oldEndVnode = oldChild[ oldEndIndex]
let mapKey = null
while ( newStartIndex <= newEndIndex && oldStartIndex <= oldEndIndex) {
console. log ( '我进入了死循环----》》》' )
if ( oldStartVnode == null || oldStartVnode === undefined ) {
oldStartVnode = oldChild[ ++ oldStartIndex] ;
}
else if ( oldEndVnode == null || oldEndVnode === undefined ) {
oldEndVnode = oldChild[ -- oldEndIndex] ;
}
else if ( newStartVnode == null || newStartVnode === undefined ) {
newStartVnode = newChild[ ++ newStartIndex] ;
}
else if ( newEndVnode == null || newEndVnode === undefined ) {
newEndVnode = newChild[ -- newEndIndex] ;
}
else if ( checkoutSameVnode ( oldStartVnode, newStartVnode) ) {
console. log ( '第一种,新前与旧前对比,只改变虚拟dom' )
patchVnode ( oldStartVnode, newStartVnode)
newStartVnode = newChild[ ++ newStartIndex]
oldStartVnode = oldChild[ ++ oldStartIndex]
} else if ( checkoutSameVnode ( oldEndVnode, newEndVnode) ) {
console. log ( '第二种,新后与旧后对比,只改变虚拟dom' , oldEndVnode, newEndVnode, oldEndIndex, oldStartIndex)
patchVnode ( oldEndVnode, newEndVnode)
oldEndVnode = oldChild[ -- oldEndIndex]
newEndVnode = newChild[ -- newEndIndex]
console. log ( '第二种,新后与旧后对比,只改变虚拟dom' , oldEndVnode, newEndVnode, oldEndIndex, oldStartIndex)
} else if ( checkoutSameVnode ( oldStartVnode, newEndVnode) ) {
console. log ( '新后与旧前命中-- 三三三 -->>>此时要移动节点,移动新前指向的这个节点到旧节点的旧后面' , oldStartVnode, newEndVnode, oldEndVnode, oldEndVnode. elm. nextSibling)
patchVnode ( oldStartVnode, newEndVnode)
parentElm. insertBefore ( oldStartVnode. elm, oldEndVnode. elm. nextSibling)
newEndVnode = newChild[ -- newEndIndex]
oldStartVnode = oldChild[ ++ oldStartIndex]
} else if ( checkoutSameVnode ( oldEndVnode, newStartVnode) ) {
console. log ( '旧后与新前==四四四===》此时要移动节点,移动新前指向的这个节点到旧节点的旧前' )
patchVnode ( oldEndVnode, newStartVnode)
parentElm. insertBefore ( oldEndVnode. elm, oldStartVnode. elm)
oldEndVnode = oldChild[ -- oldEndIndex]
newStartVnode = newChild[ ++ newStartIndex]
} else {
console. log ( 'newStartIndex' , newStartIndex, )
if ( ! mapKey) {
mapKey = { }
for ( let i = oldStartIndex ; i <= oldEndIndex; i ++ ) {
let key = oldChild[ i] . key
if ( key !== undefined ) {
mapKey[ key] = i
}
}
}
let zIndex = mapKey[ newStartVnode. key]
if ( zIndex === undefined ) {
parentElm. insertBefore ( creatElement ( newStartVnode) , oldStartVnode. elm)
} else {
let remove = oldChild[ zIndex]
patchVnode ( remove, newStartVnode)
oldChild[ zIndex] = undefined
parentElm. insertBefore ( remove. elm, oldStartVnode. elm)
}
newStartVnode = newChild[ ++ newStartIndex]
}
}
if ( newStartIndex <= newEndIndex) {
for ( let i = newStartIndex; i <= newEndIndex; i ++ ) {
parentElm. insertBefore ( creatElement ( newChild[ i] ) , oldChild[ oldStartIndex] . elm)
console. log ( 'parentElm--->' , parentElm)
}
} else if ( oldStartIndex <= oldEndIndex) {
for ( let i = oldStartIndex; i <= oldEndIndex; i ++ ) {
if ( oldChild[ i] && oldChild[ i] . elm !== undefined ) {
parentElm. removeChild ( oldChild[ i] . elm)
}
}
}
}