手写React思路及过程

146 阅读4分钟
ReactDOM.render(
 <App />,
  document.getElementById('root')
);

这里一般是react的入口,有三个步骤。

  1. 将React元素转为虚拟DOM
  2. 创建根容器fiber
  3. 将元素(App)放入根fiber的更新队列
  4. 从当前节点找到根节点
  5. 从根节点进行更新
  6. 更新使用两个对象交替
  7. 进行对比后生成真实dom
  8. 并挂载到根节点

这里先说第一步,将React元素转为虚拟DOM,如果我们在babel输入一个JSX形式的元素,babel会调用createElement方法,这个方法会生成虚拟DOM;所以我们先来实现这个方法

<div key="1">text</div>
React.createElement("div", {
  key: "1"
}, "text");
const RESOLVED_ATTR = {
  _self: true,
  key: true,
  ref: true,
}
function createElement(type, attributes, ...children) {
  let key = null;
  let ref = null;
  let props;

  for(let propsName of attributes) {
     if(propsName === key) key = attributes[propsName];
     if(propsName === ref) ref = attributes[ref];
     if(!RESOLVED_ATTR.hasOwnProperty(propsName)) {
       props[propsName] = attributes[propsName]
     }
  }
  if(children && children.length === 1) {
     props.children = children;
  } else {
    props.children = []
    for(let i = 0;i < children.length;i++) {
      props.children.push(children[i]);
    }
  }

  return {
    $$typeof: 'REACT_ELEMENT',
    type,
    props
  }
}

二 fiber节点 特征

  1. 有多个指针,比如return child sibling指针

  2. 一个fiber就是一个工作单元

  3. fiber节点是基于虚拟DOM生成的,也是树状结构,在遍历的时候要按照一定顺序。从根节点开始工作,遍历第一个子元素,子元素开始工作,如果当前子元素没有子元素的话,当前子元素结束工作,开始兄弟元素,等到所有子元素遍历完成后,父元素完成。 开始工作:根 -》 父 -》 子 结束工作:子 -》 父亲 -》 根

  4. fiber在遍历过程中会产生副作用链,父作用链会记录当前节点的操作,比如更新/创建/删除等

创建根fiber

function render(ele, container) {
  let fiberRoot = createFiberRoot(container);
}
function createFiberRoot(containerInfo) {
  const root = { containerInfo }; 
  rootFiber = createHostRootFiber(); // 创建fiber对象
  root.current = rootFiber;
  rootFiber.stateNode = root;
  initializeUpdateQueue(root); // 将root的更新对列设置为空
  return root;
}
function createHostRootFiber() {
  return createFiber('host');
}
function createFiber(tag, pendingProps, key) {
  return new FiberNode(tag, pendingProps, key);
}
function FiberNode(tag, pendingProps, key) {
  this.tag = tag;
  this.pendingProps = pendingProps;
  this.key = key;
}
function initializeUpdateQueue(fiber) {
   const updateQueue = {
     shared: {
       pending: null
     }
   }
   fiber.updateQueue = updateQueue;
}

更新根容器,即将元素放入fiber的更新队列

function render(ele, container) {
  let fiberRoot = createFiberRoot(container);
  updateContainer(ele, fiberRoot);
}
function updateContainer(element, container) {
   const current = container.current;
   const update = createUpdate(element);
   update.payload = element;
   enqueueUpdate(current, update);
   scheduleUpdateOnFiber(current);
}
function createUpdate() {
  return {};
}
// enqueueUpdate是一个循环列表
function enqueueUpdate(fiber, update) {
  const updateQueue = fiber.updateQueue;
  let pending = updateQueue.shared.pending;
  if(!pendingProps) {
    update.next = update
  } else {
    update.next = pending.next;
  }
  pendingProps = update.next;
}

从当前fiber向上找到根fiber,从根fiber执行工作循环

function scheduleUpdateOnFiber(fiber) {
  const fiberRoot = markUpdateLaneFromFiberRoot(fiber); // 向上找到根fiber
  performSyncWorkOnRoot(fiberRoot);
}

function markUpdateLaneFromFiberRoot(fiber) {
  let node = fiber;
  let parent = fiber.return;
  while(parent) {
    node = parent;
    parent = parent.parent;
  }
  return node.stateNode;
}
// 从根fiber创建新的work fiber
function performSyncWorkOnRoot(root) {
  workInProgressRoot = root;
  workInprogress = createWorkInProgress(root.current);
}
function createWorkInProgress(current, pendingProps) {
   let workInProgress = current.alternate;
   if(!workInProgress) {
    workInProgress = createFiber(current.tag, pendingProps);
    workInProgress.child = null;
    workInProgress.stateNode = current.stateNode;
    workInProgress.alternate = current;
    current.alternate = workInProgress;
   } else {
     workInProgress.pendingProps = pendingProps;
   }
   workInProgress.flag = 0;
   workInProgress.child = null;
   workInProgress.sibling = null;
   workInProgress.updateQueue = current.updateQueue;
   return workInProgress;
}

function performSyncWorkOnRoot(root) {
  workInProgressRoot = root;
  workInprogress = createWorkInProgress(root.current);
  // 执行工作循环, 构建副作用链
  workLoopSync(workInprogress);
}
function workLoopSync(workInProgress) {
  while(workInProgress) {
    performUnitOfWork(workInProgress);

  }
}
function performUnitOfWork(workInProgress) {
  const current = workInProgress.alternate;
  // 构建新的子fiber并返回
  let next = beginWork(current, workInProgress);
  if(next) {
     workInProgress = next;
  } else {
    computedUnitOfWork(current, workInProgress);

  }
}
function beginWork(current, workInProgress) {
  const type = workInProgress.tag;
   switch(type) {
     case 'host':
      return updateHostRoot(current, workInProgress);
     case 'component':
       return updateHostComponent(current, workInProgress);
     default:
       return;
   }
 
}
function updateHostRoot(current, workInProgress) {
  let nextChild = workInProgress.updateQueue.shared.pending.element;
  // 根据老的fiber和新的虚拟dom,创建新的子fiber;
  reconcileChildren(current, workInProgress, nextChild);
  return workInProgress.child;

}
  // 创建并返回第一个新的子fiber
function reconcileChildFibers(returnFiber, currentFirstChild, newChild) {
  if( nextChild && typeof nextChild === 'object') {
    switch(newChild.$$typeof) {
       case 'REACT_ELEMENT_TYPE':
       // placeSingleChild是根据current属性,为当前fiber添加副作用属性;
       // reconcileSingleFiber是根据虚拟dom,创建子fiber;
         return placeSingleChild(reconcileSingleFiber(returnFiber, currentFirstChild, newChild));
    }
    let childFiber = createFiber();
    childFiber.return = workInProgress;
    childFiber.flags = '1';
  }
}
function reconcileSingleFiber(returnFiber, currentFirstChild, newChild) {
   const create = createFiberFromElement(newChild);
   create.return = returnFiber;
   return create;
}

当新的子fiber构建完成后,生成新的dom节点,挂载到fiber上,并把当前副作用链接到父作用链上

function performUnitOfWork(workInProgress) {
  const current = workInProgress.alternate;
  // beginwork从上到下
  let next = beginWork(current, workInProgress);
  if(next) {
     workInProgress = next;
  } else {
  // computedUnitOfWork从下到上
  // 根据子fiber创建真实节点,并提交自己的副作用
    computedUnitOfWork(workInProgress);

  }
}

function computedUnitOfWork(unitOfWork) {
  let completedWork = unitOfWork;
  do{
    const current = completedWork.alternate;
    const returnedFiber = completedWork.return;
    // 完成此fiber对应的真实dom节点的创建
    completeWork(current, completedWork);
    // 收集当前fiber的副作用到父fiber
    collectEffect(returnedFiber, completedWork);
    // 当自己这个fiber完成后,寻找下一个要构建的fiber
    const siblingFiber = completedWork.sibling;
    if(siblingFiber) {
      workInProgress = siblingFiber;
      return;
    }
    completedWork = returnedFiber;
    workInProgress = completedWork;


  } while(workInProgress);

}

function completeWork(current, workInProgress) {
  const newProps = workInProgress.pendingProps;
  switch(workInProgress.tag) {
    case 'hostComponent':
    // buildComponent,通过document.createElement创建dom
      const instance = buildComponent(type, workInProgress);
      workInProgress.stateNode = instance;
      // finalizeInitialChildren为新的dom添加属性
      finalizeInitialChildren(instance, type, newProps);

  }
}

// 把自己到副作用提交到父节点上,最终返回一条作用链
function collectEffect(returnedFiber, completedWork) {
  if(!returnedFiber.firstEffect) {
    returnedFiber.firstEffect = completedWork.firstEffect;
  } 
  if(completedWork.lastEffect) {
    if(returnedFiber.lastEffect) {
      returnedFiber.lastEffect.nextEffect = completedWork.firstEffect;
    }
    returnedFiber.lastEffect = completedWork.lastEffect;
  }
  else {
    returnedFiber.lastEffect.next = completedWork.firstEffect;
  }
  const flags = completedWork.flags;
  if(flags) { 
    if(returnedFiber.lastEffect) {
      returnedFiber.lastEffect.nextEffect = completedWork;
    } else {
      returnedFiber.firstEffect =  completedWork;
    }

    returnedFiber.lastEffect = completedWork;
  }
}

构建完副作用链后,进行提交,也就是根据副作用来操作dom

function performSyncWorkOnRoot(root) {
  workInProgressRoot = root;
  workInprogress = createWorkInProgress(root.current);
  // 执行工作循环, 构建副作用链
  workLoopSync(workInprogress);
  // 提交
  commitRoot();
}
function commitRoot() {
  const finishedWork = workInProgressRoot.current.alternate;
  workInProgressRoot.finishedWork = finishedWork;
  commitMutationEffects(workInProgressRoot);
}
// 生成新的dom树,并将current指向新的fiber
function commitMutationEffects(root) {
  const finishedWork = root.finishedWork;
  let nextEffect = finishedWork.nextEffect;
  while(nextEffect) {
    const flags = nextFiber.flags;
    // 添加新的元素
    if(flags === 2) {
      const stateNode = nextEffect.stateNode;
      const parentStateNode = nextEffect.return.stateNode;
      parentStateNode.appendChild(stateNode);
  
    }
    nextEffect = nextEffect.nextEffect;

  }
  root.current = finishedWork;

}

更新

  1. 在render的时候,判断根节点是否创建,如果创建了就复用,没有的话新建
function render(ele, container) {
  let fiberRoot = container.__reactRootContainer;
  if(!fiberRoot) fiberRoot = container.__reactRootContainer = createFiberRoot(container);
  updateContainer(ele, fiberRoot);

}
// 在当前的fiber中,添加更新队列
function updateContainer(element, container) {
   const current = container.current;
   const update = createUpdate(element);
   update.payload = element;
   enqueueUpdate(current, update);
   scheduleUpdateOnFiber(current);
}
// 调度更新,也就是创建新的fiber
function scheduleUpdateOnFiber(fiber) {
  const fiberRoot = markUpdateLaneFromFiberRoot(fiber);
  performSyncWorkOnRoot(fiberRoot);
  
}
function performSyncWorkOnRoot(root) {
  workInProgressRoot = root;
  workInprogress = createWorkInProgress(root.current);
  // 执行工作循环, 构建副作用链
  workLoopSync(workInprogress);
  // 提交
  commitRoot();
}

这里属于开始阶段

这里和之前初次渲染时不一样,如果不是初次渲染,并且只生成了一个虚拟DOM,需要对比之前fiber的key和type,来决定是更新还是删除

function reconcileSingleFiber(returnFiber, currentFirstChild, element) {
  let key = element.key;
  let child = currentFirstChild;
  while(child) {
    if(child.key === key) {
        if(child.type === element.type) {
          deleteAllChild(returnFiber, child.sibling);
          // 复用老的节点和新的属性,返回更新后的fiber
          const existing = useFiber(child, element.props);
          existing.return = returnFiber;
          return existing;
        } else {
        // 所有都不能复用
          deleteAllChild(returnFiber, child);
          break; 
        }
    } else {
    // 当前fiber不能复用
      deleteChild(returnFiber, child);
    }
    child = child.sibling;

  }
  // 如果不能复用的话,构建新的fiber,并返回
   const create = createFiberFromElement(newChild);
   create.return = returnFiber;
   return create;
}

function useFiber(oldFiber, props) {
  return createWorkInProgress(oldFiber, props);
}
function deleteAllChild(returnFiber, child) {
  while(child) {
    deleteChild(returnFiber, child);
    child = child.sibling;
  }
}
// 标记为删除,并在父节点上添加副作用,目的是放在链表的头部 
function deleteChild(returnedFiber, child) {
  child.nextEffect = null;
  child.flags = '3';
  let lastEffect = returnedFiber.lastEffect;
  if(lastEffect) {
    returnedFiber.nextEffect = child;
    returnedFiber.lastEffect = child;
  } else {
    returnFiber.firstEffect = returnedFiber.lastEffect = child;
  }
}

这里属于完成阶段

function completeWork(current, workInProgress) {
  const newProps = workInProgress.pendingProps;
  switch(workInProgress.tag) {
    case 'hostComponent':
      // 节点可复用
      if(current && workInProgress.stateNode) {
        // 复用节点,更新属性
        updateHostComponent(current, workInProgress, workInProgress.tag, newProps);
      }
      const instance = buildComponent(current, workInProgress);
      workInProgress.stateNode = instance;
      finalizeInitialChildren(instance, type, newProps);

  }
}

// 更新属性
function updateHostComponent(current, workInProgress, tag, newProps) {
  let oldProps = current.memoizedProps;
  let instance = workInProgress.stateNode;
  // 返回属性数组
  const updatePayload = diffProperties(instance, tag, oldProps, newProps);
  // 把更新数组添加到更新队列中,提交阶段可以拿来用
  // 根fiber的更新队列和原生fiber的更新队列不一样,原生fiber [key1,value1, key2, value2 ],根fiber是一个循环链表
  workInProgress.updateQueue = updatePayload; 
  if(updatePayload) {
    workInProgress.flags |= 0;
  }
}
// 返回更新的属性数组
function diffProperties(domElement, tag, lastProps, newProps) {
  let updatePayload = null;
  let propKey;
  for(propKey in lastProps) {
    if(lastProps[propKey] && !newProps[propKey]) {
      (updatePayload = updatePayload || []).push(propKey, null);
    }
  }
  for(propKey in newProps) {
    if(propKey === 'children') {
      if(typeof newProps[propKey] === 'string' || typeof newProps[propKey] === 'number') {
        if(newProps[propKey] !== oldProps[propKey]) {
          (updatePayload = updatePayload || []).push(propKey, newProps[propKey]);
        }
      }
      
    } else {
       if(newProps[propKey] !== lastProps[propKey]) {
        (updatePayload = updatePayload || []).push(propKey, newProps[propKey]);
       }
    }
  }
  return updatePayload;
}

提交阶段

// 从根节点进行操作,看是否有副作用,
function commitMutationEffects(root) {
  const finishedWork = root.finishedWork;
  let nextEffect = finishedWork.nextEffect;
  while(nextEffect) {
    const flags = nextFiber.flags;
    let current = nextEffect.alternate;
    // 添加
    if(flags === 2) {
      const stateNode = nextEffect.stateNode;
      const parentStateNode = nextEffect.return.stateNode;
      parentStateNode.appendChild(stateNode);
    } else if(flags === 1) {
    // 删除
      commitDeletion(nextEffect);
    } else if(flags === 3) {
      // 更新
      commitWork(current, nextEffect);
    }
    nextEffect = nextEffect.nextEffect;
  }
  root.current = finishedWork;
}

// 更新dom操作
function commitWork(current, finishedWork) {
   // 获取属性更新数组
   const updatePayload = finishedWork.updateQueue;
   finishedWork.updateQueue = null;
   if(updatePayload) {
    updateProperties(finishedWork.stateNode, updatePayload);
   }
}
function updateProperties(domElement, updatePayload) {
  for(let i = 0;i < updatePayload.length;i+=2) {
    let propKey = updatePayload[i];
    let propVal = updatePayload[i + 1];
    if(propKey === 'children') {
      domElement.textContent = propVal;
    } else domElement.setAttribute(propKey, propVal);
  }
}
// 删除dom操作
function commitDeletion(fiber) {
  if(!fiber) return;
  let parentStateNode = getParentStateNode(fiber);
  parentStateNode.removeChild(fiber.stateNode);
}