【React 16】模拟实现5

React | 2020-06-21 16:33:04 461次 1次

本篇实现类组件渲染更新,hooks 的简单实现。

一、类组件

import React from './react';
import ReactDOM from './react-dom';

class ClassCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { number: 0 };
  }
  onClick = () => {
    this.setState(state => ({ number: state.number + 1 }));
  }
  render() {
    return (
      <div id="counter">
        <span>{this.state.number}</span>
        <button onClick={this.onClick}>加1</button>
      </div>
    )
  }
}
ReactDOM.render(<FunctionCounter name="计数器" />, document.getElementById('root'));

假设我们有一个简单的计数器组件,继承自 React.component,所以需要在 react.js 中添加这个类:

...
import { Update } from './UpdateQueue';
...

...
class Component {
    constructor(props) {
        this.props = props;
    }
    setState(payload) {//可能是对象,也可能是一个函数
        let update = new Update(payload);
        //updateQueue其实是放在此类组件对应的fiber节点的 internalFiber
        this.internalFiber.updateQueue.enqueueUpdate(update);
        scheduleRoot();//从根节点开始调度
    }
}
Component.prototype.isReactComponent = {};//类组件
...

UpdateQueue.js:

export class Update {
    constructor(payload) {
        this.payload = payload;
    }
}
//数据结构是一个单链表
export class UpdateQueue {
    constructor() {
        this.firstUpdate = null;
        this.lastUpdate = null;
    }
    enqueueUpdate(update) {
        if (this.lastUpdate === null) {
            this.firstUpdate = this.lastUpdate = update;
        } else {
            this.lastUpdate.nextUpdate = update;
            this.lastUpdate = update;
        }
    }
    forceUpdate(state) {
        let currentUpdate = this.firstUpdate;
        while (currentUpdate) {
            let nextState = typeof currentUpdate.payload === 'function' 
                ? currentUpdate.payload(state) : currentUpdate.payload;
            state = { ...state, ...nextState };
            currentUpdate = currentUpdate.nextUpdate;
        }
        this.firstUpdate = this.lastUpdate = null;
        return state;
    }
}

实现类组件的时候,定义的那个组件类型仅仅是一个标识,虽然也是一个fiber节点,但是其无法去更新,需要把自己的 renderreturn 出的节点挂载到自己父级上,继续完善 scheduler.js

...
function beginWork(currentFiber) {
    ...
    } else if (currentFiber.tag === TAG_CLASS) {//类组件
        updateClassComponent(currentFiber);
    }
}

function updateClassComponent(currentFiber) {
    if (!currentFiber.stateNode) {//类组件 stateNode 组件的实例
        // new ClassCounter(); 类组件实例   fiber双向指向 
        currentFiber.stateNode = new currentFiber.type(currentFiber.props);
        currentFiber.stateNode.internalFiber = currentFiber;
        currentFiber.updateQueue = new UpdateQueue();
    }
    //给组件的实例的state 赋值
    currentFiber.stateNode.state = currentFiber.updateQueue.forceUpdate(currentFiber.stateNode.state);
    let newElement = currentFiber.stateNode.render();
    const newChildren = [newElement];
    reconcileChildren(currentFiber, newChildren);
}
...
...
//这里因为更新节点时候 ClassCounter这个fiber不需要更新
function updateDOM(stateNode, oldProps, newProps) {
    if (stateNode && stateNode.setAttribute)
        setProps(stateNode, oldProps, newProps);
}
...
...
function reconcileChildren(currentFiber, newChildren) {//[A1]
    ...
    while (newChildIndex < newChildren.length || oldFiber) {
        ...
        if (newChild && typeof newChild.type === 'function' && newChild.type.prototype.isReactComponent) {
            tag = TAG_CLASS;//
        } 
        ...
        if (sameType) {
            if (oldFiber.alternate) {//说明至少已经更新一次了
                ...
                newFiber.updateQueue = oldFiber.updateQueue || new UpdateQueue();
                ...
            } else {
                newFiber = {
                    ...
                    updateQueue: oldFiber.updateQueue || new UpdateQueue(),
                    ...
                }
            }
        } else {
            if (newChild) {//看看新的虚拟DOM是不是为null
                newFiber = {
                    ...
                    updateQueue: new UpdateQueue(),
                    ...
                }
            }
            ...

        }
       ...
    }
}
...
function commitWork(currentFiber) {
    ...
    // 跳过继承的这个类组件,因为它只是一个标识
    while (returnFiber.tag !== TAG_HOST &&
        returnFiber.tag !== TAG_ROOT &&
        returnFiber.tag !== TAG_TEXT) {
        returnFiber = returnFiber.return;
    }
    ...
    if (currentFiber.effectTag === PLACEMENT) {//新增加节点
            let nextFiber = currentFiber;
            if (nextFiber.tag === TAG_CLASS) {
                return;
            }
            // 如果要挂载的节点不是DOM节点,比如说是类组件Fiber,一直找第一个儿子,直到找到一个真实DOM节点为止
            while (nextFiber.tag !== TAG_HOST && nextFiber.tag !== TAG_TEXT) {
                nextFiber = currentFiber.child;
            }
            domReturn.appendChild(nextFiber.stateNode);
    //删除节点的时候需要跳过ClassCounter,直接找子节点
    } else if (currentFiber.effectTag === DELETION) {//删除节点
        return commitDeletion(currentFiber, domReturn);
    }
    ...
}
function commitDeletion(currentFiber, domReturn) {
    if (currentFiber.tag == TAG_HOST || currentFiber.tag == TAG_TEXT) {
        domReturn.removeChild(currentFiber.stateNode);
    } else {
        commitDeletion(currentFiber.child, domReturn)
    }
}



二、HOOKS函数组件

添加完这些逻辑,实现类组件的渲染更新,接下来继续实现 hooksuseReducer 和 useState 功能,函数组件直接调用即可

const ADD = 'ADD';
function reducer(state, action) {
  switch (action.type) {
    case ADD:
      return { count: state.count + 1 };
    default:
      return state;
  }
}
function FunctionCounter() {
  const [numberState, setNumberState] = React.useState({ number: 0 });//0
  const [countState, dispatch] = React.useReducer(reducer, { count: 0 });//0

  return (
    <div>
      <div id="counter1">
        <span>{numberState.number}</span>
        <button onClick={() => setNumberState({ number: numberState.number + 1 })}>加1</button>
      </div>
      <div id="counter2">
        <span>{countState.count}</span>
        <button onClick={() => dispatch({ type: ADD })}>加1</button>
      </div>
    </div>
  )
}
ReactDOM.render(<FunctionCounter name="计数器" />, document.getElementById('root'));
...
let workInProgressFiber = null;//正在工作中的fiber
let hookIndex = 0;//hooks索引 
...
function beginWork(currentFiber) {
    ...
    } else if (currentFiber.tag === TAG_FUNCTION_COMPONENT) {//函数组件
        updateFunctionComponent(currentFiber);
    }
}
function updateFunctionComponent(currentFiber) {
    workInProgressFiber = currentFiber;
    hookIndex = 0;
    workInProgressFiber.hooks = [];
    const newChildren = [currentFiber.type(currentFiber.props)];
    reconcileChildren(currentFiber, newChildren);
}
...
export function useReducer(reducer, initialValue) {
    let newHook = workInProgressFiber.alternate && workInProgressFiber.alternate.hooks
        && workInProgressFiber.alternate.hooks[hookIndex];
    if (newHook) {
        newHook.state = newHook.updateQueue.forceUpdate(newHook.state);
    } else {
        newHook = {
            state: initialValue,
            updateQueue: new UpdateQueue()// 空的更新队列
        }
    }
    const dispatch = action => {//{type:'ADD'}
        let payload = reducer ? reducer(newHook.state, action) : action;
        newHook.updateQueue.enqueueUpdate(
            new Update(payload)
        );
        scheduleRoot();
    }
    workInProgressFiber.hooks[hookIndex++] = newHook;
    return [newHook.state, dispatch];
}
export function useState(initialValue) {
    return useReducer(null, initialValue);
}

react 的 hooks 简单模拟实现完成,具体代码请移步 源码地址https://github.com/wineSu/react-Hooks


1人赞

分享到: