【React】React源碼梳理筆記(四)

前言

  • 繼續上次寫的React。這次是屬性更新。

普通元素更新屬性

  • 首先把上次應該寫覆蓋比較的地方改成一個函數:
function compareTwoElement(oldelement,newelement){
    let currentDom = oldelement.dom 
    let currentElement = oldelement
    if(newelement===null){//如果爲空,直接刪除
        currentDom.parentNode.removeChild(currentDom)
        currentDom= null
        currentElement=null
    }else if(oldelement.type!== newelement.type){//新舊類型不一樣
        let newDom = createDOM(newelement)
        currentDom.parentNode.replaceChild(newDom,currentDom)
        currentElement=newelement //把當前虛擬dom換成新的
    }else{ //類型一樣,那就可以複用,所以深度比較
        updateElement(oldelement,newelement)
    }
    return currentElement
}
  • 走這個方法,代表節點可以複用。所以儘可能複用節點。
function updateElement(oldelement,newelement ){//
    let currentDom =newelement.dom =oldelement.dom //在這裏的就可以複用dom了。
    if(oldelement.$$typeof===REACT_TEXT_TYPE&&newelement.$$typeof===REACT_TEXT_TYPE){//文本比較
        currentDom.textContent=newelement.type//修改文本
    }else if(oldelement.$$typeof===REACT_ELEMENT_TYPE){//元素類型
        updateDomProperties(currentDom,oldelement.props,newelement.props)
    }
}
function updateDomProperties(dom,oldprops,newprops){
    patchProps(dom,oldprops,newprops)
}
function patchProps(dom,oldprops,newprops){
    for (let key in oldprops){//循環老屬性
        if(key!=='children'){
            if(!newprops.hasOwnProperty(key)){//不存在則刪除
                dom.removeAttribute(key)
            }
        }
    }
    for (let key in newprops){//循環新屬性
        if(key!=='children'){
            setProp(dom,key,newprops[key])//賦值
        }
    }
}
  • 這樣button的id就會隨着state的變化而變化了。

函數組件更新屬性

  • 先做個函數組件看效果用。
function FunctionCounter(props){
  return (
    <div id={'counter'+props.number}>
      <p>{props.number}</p>
      <button onClick={props.handleClick}>+</button>
    </div>
  )
}

class Counter extends React.Component{
  constructor(props){
    super(props)
    this.state={number:0}
    console.log(this)
  } 
  handleClick=()=>{
    this.setState((state)=>({number:state.number+1}))
    console.log(this.state)
  }
  render(){
     return <FunctionCounter number={this.state.number} handleClick={this.handleClick}></FunctionCounter>
  }
}
  • 還是老地方,如果老節點是個函數組件,新節點和它類型一樣,那麼新節點也是函數組件:
function updateElement(oldelement,newelement ){//
    let currentDom =newelement.dom =oldelement.dom //在這裏的就可以複用dom了。
    if(oldelement.$$typeof===REACT_TEXT_TYPE&&newelement.$$typeof===REACT_TEXT_TYPE){//文本比較
        currentDom.textContent=newelement.type//修改文本
    }else if(oldelement.$$typeof===REACT_ELEMENT_TYPE){//元素類型
        updateDomProperties(currentDom,oldelement.props,newelement.props)
    }else if(oldelement.$$typeof===FUNCTION_COMPONENT){//類型都是函數組件
        updateFunctionComponent(oldelement,newelement)
    }
}
  • 直接先運行下,取出渲染結果,爲了避免多次運行,所以要把老函數組件的虛擬dom上掛上渲染後的結果。
  • 拿到2者虛擬dom,重新去對比就行。
function createFunctionDOM(element){
    let {type,props}=element
    let renderElement = type(props)
    let newDom =createDOM(renderElement)
    element.renderElement=renderElement//防止多次去取
    return newDom
}
function updateFunctionComponent(oldelement,newelement){
    let oldRenderElement = oldelement.renderElement//取出老函數渲染結果
    let newRenderElement =newelement.type(newelement.props)//新函數渲染結果
    let currentElement =compareTwoElement(oldRenderElement,newRenderElement)//重新比較拿到最終結果
    newelement.renderElement=currentElement
}
  • 這樣就ok了

類組件更新屬性

  • 老樣子,完成後面判斷:
function updateElement(oldelement,newelement ){//
    let currentDom =newelement.dom =oldelement.dom //在這裏的就可以複用dom了。
    if(oldelement.$$typeof===REACT_TEXT_TYPE&&newelement.$$typeof===REACT_TEXT_TYPE){//文本比較
        currentDom.textContent=newelement.type//修改文本
    }else if(oldelement.$$typeof===REACT_ELEMENT_TYPE){//元素類型
        updateDomProperties(currentDom,oldelement.props,newelement.props)
    }else if(oldelement.$$typeof===FUNCTION_COMPONENT){//類型都是函數組件
        updateFunctionComponent(oldelement,newelement)
    }else if(oldelement.$$typeof===CLASS_COMPONENT){//類型都是函數組件
        updateClassComponent(oldelement,newelement)
    }
}
  • 同樣防止取多次
function createClassComponetDOM(element){
    let {type,props}=element
    let componentInstance =new  type(props)
    let renderElement = componentInstance.render()
    componentInstance.renderElement=renderElement
    element.componentInstance=componentInstance
    let newDom =createDOM(renderElement)
    return newDom
}
function updateClassComponent(oldelement,newelement){
    let componentInstance = oldelement.componentInstance//拿到實例
    let updater = componentInstance.updater//實例裏new的那個updater
    let nextProps = newelement.props  // 新的屬性
    updater.emitUpdate(nextProps)//setstate會走這個判斷,這個同樣直接判斷.讓updater去更新類組件
}
  • 這樣就能成功更新類組件屬性了。

  • 剩下的下篇寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章