createElemnet生成Elemnet对象
//封装createElement
class Element {
constructor(type,props,children){
this.type=type;
this.props=props;
this.children=children;
}
};
//返回object的
function createElement(type,props,children){
return new Element(type,props,children);
};
render将对象渲染成真实DOM
//将虚拟dom转化成真实DOM
function render(eleObj){
let el=document.createElement(eleObj.type);
for(let key in eleObj.props){
//设置属性的方法
setAttr(el,key,eleObj.props[key])
}
//遍历儿子 如果是虚拟DOM 就继续渲染,如果不是就代表是文本节点
eleObj.children.forEach((child)=>{
console.log(child)
if(child instanceof Element){
child=render(child);
}else{
child=document.createTextNode(child)
}
el.appendChild(child)
})
return el;
}
let vertual=render(vertualDom)
console.log()
//将虚拟DOM转化成的真实DOM 绑定到页面上
function renderDom(el,target){
target.appendChild(el)
}
renderDom(vertual,document.querySelector('#app'))
diff的比较规则
1.比较属性是否相同 {type:'ATTRS',attrs:{class:'aa'}}
2.新的DOM节点不存在{type:'REMOVE',index:3}
3.节点类型不相同 直接采用替换的模式{type:'REPLACE',newNode:newNode}
4.文本的变化{type:'TEXT',text:1}
diff比较两个Elemnet对象,生成补丁
const ATTRS='ATTRS';
const TEXT='TEXT';
const REMOVE='REMOVE';
const REPLACE='REPLACE';
let Index=0;
function diff(oldTree,newTree){
let patches={};
let index=0;
//递归树,比较后的结果放到补丁包中
walk(oldTree,newTree,index,patches)
return patches
}
function walk(oldTree,newTree,index,patches){
let currentPatch=[];
if(!newTree){//新节点被删除
currentPatch.push({type:'REMOVE',index:index})
}else if((typeof oldTree == 'string')&& (typeof newTree == 'string')){//新节点是文本
if(oldTree !== newTree){//判断文本是不是发生变化了
currentPatch.push({type:'TEXT',text:newTree})
}
}else if(oldTree.type==newTree.type){//如果节点相同就比属性
//比较属性是否有更改
let attrs=diffAttr(oldTree.props,newTree.props)
if(Object.keys(attrs).length>0){//说明属性有变化
currentPatch.push({type:ATTRS,attrs:attrs})
}
//如果有儿子节点 遍历儿子节点
diffChildren(oldTree.children,newTree.children,index,patches);
}else{//节点不同
currentPatch.push({type:'REPLACE',newNode:newTree})
}
if(currentPatch.length>0){//当前元素确实有补丁
//将元素和补丁对应起来 放到大补丁中
patches[index]=currentPatch
}
}
function diffAttr(oldAttrs,newAttrs){
let patch={};
for(let key in oldAttrs){
//老节点的属性发生变化
if(oldAttrs[key]!==newAttrs[key]){
patch[key]=newAttrs[key]//有可能是undefined
}
}
for(let key in newAttrs){
//老节点没有新节点的属性
if(!oldAttrs.hasOwnProperty(key)){
patch[key]=newAttrs[key]
}
}
return patch
}
function diffChildren(oldChildren,newChildren,index,patches){
//比较老的第一个和新的第一个
oldChildren.forEach((child,idx)=>{
console.log(++index,204)
walk(child,newChildren[idx],++Index,patches)
})
}
let patches=diff(vertualDom1,vertualDom2)//得到两棵DOM树的补丁包
console.log(patches)
patch打补丁,重新渲染真实DOM
let index=0;//默认哪个需要打补丁
function patch(node , patches){
console.log(node)
//给某个元素打补丁
console.log(patches)
walkpat(node,patches)
}
function walkpat(node,patches){
console.log(patches)
let currentPatch=patches[index++];
let childNodes=node.childNodes;
childNodes.forEach((child)=>{
walkpat(child,patches);
})
if(currentPatch&¤tPatch.length>0){
doPach(node,currentPatch)
}
}
function doPach(node,patches){
patches.forEach(patch=>{
switch(patch.type){
case 'ATTRS':
for(let key in patch.attrs){
let value=patch.attrs[key]
if(value){
setAttr(node,key,value);
}else{
node.removeAttribute(key);
}
}
patch.attrs
break;
case 'TEXT':
node.textContent=patch.text;
break;
case 'PEPLACE':
let newNode=patch.newNode instanceof Element?render(patches.newNode):document.createTextNode(patch.newNode)
break;
case 'REMOVE':
node.parentNode.removeChild(node)
break;
default:
break;
}
})
}
patch(vertual,patches)
封装的缺陷
1.如果凭借元素有互换,那么会导致重新渲染
2.新增节点不会更新
网友评论