一、虚拟DOM
1.1、h() ==》虚拟节点Vnode
h( sel , data ,c )
sel: 选择器 ; data:属性(class,id,key…); c:文本节点 或者 虚拟节点h()
返回虚拟节点:Vnode
-
文件 h.js
export default function(sel,data,c){ if(arguments.length!=3){ throw new Error('传入的参数个数必须是三个'); }else if(typeof c === 'string' || typeof c === 'number'){ // h('div',{},'文字') return vnode(sel,data,undefined,c,undefined); }else if(Array.isArray(c) ){ // h('div',{},[]) let children = []; // debugger for(let i = 0;i<c.length;i++){ if(!(typeof c[i] === 'object' && c[i].hasOwnProperty('sel'))) throw new Error('第三个参数的内嵌类型不对'); children.push(c[i]); } return vnode(sel,data,children,undefined,undefined); }else if(typeof c ==='object' && c.hasOwnProperty('sel')){ // h('div',{},h()) let children = [c]; return vnode(sel,data,children,undefined,undefined); }else{ throw new Error('第三个参数类型不对'); } }
-
文件 vnode.js
export default function(sel,data,children,text,elm){ let key = data.key; return {sel,data,children,text,elm,key} }
1.2、将虚拟节点 上树(显现到界面上)
- patch.js
- 判断同一节点:选择器相同 && key 相同
export default function(oldVode,newVonde){
// debugger
// 判断oldVode 是否为虚拟节点
if(oldVode.sel == '' || oldVode.sel === undefined){
// 将dom 节点包装成虚拟节点
oldVode = vnode(oldVode.tagName.toLowerCase(),{},[],undefined,oldVode) ;
}
// -------------上树 :同时进行 Diff 更新虚拟DOM------------------------
// 判断是否为同意节点
if(oldVode.sel === newVonde.sel && oldVode.key === newVonde.key){
console.log('是同一个节点');
// 如果oldVode === newVonde Y:什么也不用
patchVnode(oldVode,newVonde);
}else{
// 暴力插入新节点,删除旧节点
let newDom = createElement(newVonde);
// 上树
oldVode.elm.parentNode.insertBefore(newDom,oldVode.elm);
// 删除老节点
oldVode.elm.parentNode.removeChild(oldVode.elm);
}
};
- patchVnode.js
export default function patchVnode(oldVode,newVonde){
if(oldVode === newVonde){
// patch(myVonde1, myVonde1);
return;
}
// patch(myVonde1, myVonde2);
// 判断 newVonde 是否有文字属性:
if(newVonde.text != undefined && (newVonde.children == undefined || newVonde.children.length ==0)){
// newVnode有文字属性
console.log('新的虚拟节点有文字属性');
if(newVonde.text != oldVode.text){
oldVode.elm.innerText = newVonde.text;
}
}else{
// 新newVnode 有children 子节点
if(oldVode.children!=undefined && oldVode.children.length>0){
// 精细比较
// newVnode 有children,oldVnode有children
console.log('newVnode 有children,oldVnode有children');
/*遍历children
1.Diff比较策略,比较当前当前层次的虚拟节点,移动节点位置
a.新前 === 旧前
b.新后 === 旧后
c.新后 === 旧前
d.新前 === 旧后
循环
2.然后判断节点的children(children.children),递归patchVnode
*/
}else{
// newVnode 有children,oldVnode没有children
// 第一步:清空oldVnode的text
oldVode.removeChild(oldVode.elm);
// 第二步:oldVnode追加 newVnode.children
for(let i = 0;i<newVonde.children.length;i++){
let newD = createElement(newVonde.children[i]);
oldVode.appendChild(newD);
}
oldVode.removeChild(oldVode.elm);
}
}
}
二、Diff算法策略
- 1.Diff比较策略,比较当前当前层次的虚拟节点,移动节点位置
- a.新前 === 旧前
- b.新后 === 旧后
- c.新后 === 旧前
- d.新前 === 旧后
- 循环
- 2.然后判断节点的children(children.children),递归patchVnode