Lophole 2020-06-10
这是Vue中vnode实例对象中设计包含的所有属性和方法
class VNode {
constructor(tag,data,children,text,elm,context,componentOptions,asyncFactory){
this.tag = tag //节点名称 eg:p、ul、div
this.data = data //节点上的数据 eg:attrs、class、style
this.children = children //当前节点的子节点列表
this.text = text //文本
this.elm = elm //vnode对应的真实的dom节点
this.ns = undefined //
this.context = context //当前组件的实例
this.functionalContext = undefined //
this.functionalOptions = undefined //
this.functionalScopeId = undefined //
this.key = data && data.key //vnode标记 在diff过程中提高diff效率
this.componentOptions = componentOptions //组件节点的选项参数 eg:propsData、tag、children
this.componentInstance = undefined //组件的实例
this.parent = undefined //
this.raw = false //
this.isStatic = false //
this.isRootInsert = false //
this.isComment = false // 是否为注释
this.isCloned = false // 是否为克隆
this.isOnce = false //
this.asyncFactory = asyncFactory //
this.asyncMeta = undefined //
this.isAsyncPlaceholder = false //
}
getChild(){
return this.componentInstance
}
}class Element {
constructor(type,props,children){
this.type = type
this.props = props
this.children = children
}
}
function createElement(type,props,children){
return new Element(type,props,children)
}
/**
* 渲染虚拟dom
*/
function render(domObj){
let el = document.createElement(domObj.type)
for(let key in domObj.props){
setAttr(el,key,domObj.props[key])
}
domObj.children.forEach((child)=>{
child = (child instanceof Element) ? render(child) : document.createTextNode(child)
el.appendChild(child)
})
return el
}
/**
* setAttr方法实现
*/
function setAttr(node,key,value){
switch(key){
case ‘value‘:
if(node.tagName.toLowerCase() == ‘input‘ || node.tagName.toLowerCase() == ‘textarea‘){
node.value = value
}else{
node.setAttribute(key,value)
}
break;
case ‘style‘:
node.style.cssText = value
break;
default:
node.setAttribute(key,value)
break;
}
}
/**
* 渲染dom方法
*/
function renderDom(el,target){
target.appendChild(el)
}
// 执行以下看看效果
// 像不像render函数的写法 哈哈哈
let vNode = createElement(‘ul‘,{class:‘list‘},[
createElement(‘li‘,{class:‘item‘},[‘乔丹‘]),
createElement(‘li‘,{class:‘item‘},[‘科比‘]),
createElement(‘li‘,{class:‘item‘},[‘韦德‘])
]);
let el = render(vNode)
let root = document.getElementById(‘zjy‘)
renderDom(el,root)//判断是否为string
function isString(str){
return typeof str === ‘string‘
}
//不同attr
function diffAttr(oldAttr,newAttr){
let patch = {}
for(let key in oldAttr){
if(oldAttr[key] !== newAttr[key]){
patch[key] = newAttr[key]
}
}
for(let key in newAttr){
if(!oldAttr.hasOwnProperty(key)){
patch[key] = newAttr[key]
}
}
return patch
}
//不同children
let num = 0
function diffChildren(oldChildren,newChildren,patches){
oldChildren.forEach((child,index)=>{
walk(child,newChildren[index],++num,patches)
})
}
function diff(oldTree,newTree){
let patches = {} //存放补丁的对象
let index = 0
walk(oldTree,newTree,index,patches)
return patches
}
/**
* walk方法中diff的情况相当于只是列举了几种情况,我在控制台执行了下,的确有的情况没有体现出来,例如删除 新增子节点,
* 所以下面的patch补丁方法中进行补丁的类别完全是按照diff中创在的不同type类型执行的不同操作
*/
function walk(oldNode,newNode,index,patches){
let current = []
if(!newNode){
current.push({type:‘REMOVE‘,index})
}else if(isString(oldNode) && isString(newNode)){
if(oldNode !== newNode){
current.push({tyep:‘TEXT‘,text:newNode})
}
}else if(oldNode.type === newNode.type){
let attr = diffAttr(oldNode.props,newNode.props)
if(Object.keys(attr).length > 0){
current.push({type:‘ATTR‘,attr})
}
diffChildren(oldNode.children,newNode.children,patches)
}else{
current.push({type:‘REPLACE‘,newNode})
}
if(current.length){
patches[index] = current
}
}
/**
* patch补丁(patch补丁简单实现的原由是因为diff方法简单实现造成的,有兴趣的可以去了解下vue中实现的diff算法 超长~~)
*/
let allPatches
let index = 0
function patch(node,patches){
allPatches = patches
walk(node)
}
function walk(node){
let current = allPatches[index++]
let childNodes = node.childNodes
childNodes.forEach((child)=>{
walk(child)
})
if(current){
doPatch(node,current)
}
}
function doPatch(node,patches){
patches.forEach((patch)=>{
switch(patch.type){
case ‘ATTR‘:
for(let key in patch.attr){
let value = patch.attr[key]
if(value){
setAttr(node,key,value)
}else{
node.removeAttribute(key)
}
}
break;
case ‘TEXT‘:
node.textContent = patch.text
break;
case ‘REPLACE‘:
let newNode = patch.newNode
newNode = (newNode instanceof Element) ? render(newNode) : document.createTextNode(newNode)
node.parentNode.replaceChild(newNode,node)
break;
case ‘REMOVE‘:
node.parentNode.removeChild(node)
break;
default:
break;
}
})
}Vue和React是数据驱动视图,如何有效控制DOM操作?能不能把计算,更多的转移为js计算?因为js执行速度很快。patch函数-->patch,对比tag,对比tag与key,对比children