前言
- 繼續上次寫的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去更新類組件
}
-
這樣就能成功更新類組件屬性了。
-
剩下的下篇寫。