React技术

React深入7-源码3(React Hook)

2022-03-07  本文已影响0人  申_9a33

写在前面


1.React 实现更新


1.1 更新Fiber

export interface IFiber{
  type: any, // 类型
  key?: string | number, // 标记当前层唯一值
  props?: any, // 属性值
  index?: number,
  child?: IFiber, // 第一个孩子
  sibling?: IFiber | null, // 下一个兄弟
  return?: IFiber, // 父节点
  stateNode: HTMLElement | null, // 真实Dom
  flags?:Number, // 标记当前节点类型(插入,更新,删除)
  alternate?:IFiber, // 老节点
  memoizedState?:any
}
export function createFiber(vnode:any, returnFiber:IFiber):IFiber {
  const newFiber = {
    type: vnode.type,
    key: vnode.key,
    props: vnode.props,
    return: returnFiber,
    stateNode: null,
    flags: Placement,
  };
  return newFiber;
}

1.2 更新 updateNode ,使其具有更新功能,并且能够处理事件

// src\react\utils.ts

// 更新原生标签的属性,如className、href、id、(style、事件)等
export function updateNode(node:any, prevVal:any, nextVal:any) {
  Object.keys(prevVal)
  // .filter(k => k !== "children")
    .forEach((k) => {
      if (k === 'children') {
        // 有可能是文本
        if (isStringOrNumber(prevVal[k])) {
          node.textContent = '';
        }
      } else if (k.slice(0, 2) === 'on') {
        const eventName = k.slice(2).toLocaleLowerCase();
        node.removeEventListener(eventName, prevVal[k]);
      } else if (!(k in nextVal)) {
        node[k] = '';
      }
    });
  Object.keys(nextVal)
  // .filter(k => k !== "children")
    .forEach((k) => {
      if (k === 'children') {
        // 有可能是文本
        if (isStringOrNumber(nextVal[k])) {
          node.textContent = `${nextVal[k]}`;
        }
      } else if (k.slice(0, 2) === 'on') {
        const eventName = k.slice(2).toLocaleLowerCase();
        node.addEventListener(eventName, nextVal[k]);
      } else {
        node[k] = nextVal[k];
      }
    });
}

1.3 修改ReactFiberReconciler

// src\react\ReactFiberReconciler.ts

export function updateFunctionComponent(wip:any) {
  renderHooks(wip);
  const { type, props } = wip;
  const children = type(props);

  reconcileChildren(wip, children);
}

function reconcileChildren(returnFiber:any, children:any) {
  if (isStringOrNumber(children)) {
    return;
  }

  const newChildren = Array.isArray(children) ? children : [children];
  let previousNewFiber:any | null = null;

  let oldFiber = returnFiber.alternate && returnFiber.alternate.child;
  // eslint-disable-next-line no-plusplus
  for (let i = 0; i < newChildren.length; i++) {
    const child = newChildren[i];
    const newFiber = createFiber(child, returnFiber);

    if (sameNode(newFiber, oldFiber)) {
      // 更新
      Object.assign(newFiber, {
        alternate: oldFiber,
        stateNode: oldFiber.stateNode,
        flags: Update,
      });
    }

    if (oldFiber) {
      oldFiber = oldFiber.sibling;
    }

    if (previousNewFiber === null) {
      // parentFiber的child指向第一个fiber
      returnFiber.child = newFiber;
    } else {
      // 否则将上一个fiber,指向当前fiber
      previousNewFiber.sibling = newFiber;
    }
    previousNewFiber = newFiber;
  }
}

function sameNode(a:any, b:any) {
  return !!(a && b && a.type === b.type && a.key === b.key);
}

1.4 修改ReactFiberWorkLoop

// src\react\ReactFiberWorkLoop.ts
export function scheduleUpdateOnFiber(fiber:any) {
  fiber.alternate = { ...fiber };
  wipRoot = fiber;
  wipRoot.sibling = null;
  nextUnitOfwork = wipRoot;
  schedulerCallback(workLoop);
}

function commitWorker(wip:any) {
  if (!wip) {
    return;
  }

  // 将子节点挂载在父节点上
  const { stateNode, flags } = wip;
  const parentNode = getParentNode(wip);

  // 新增
  if (stateNode && flags & Placement) {
    parentNode.appendChild(stateNode);
  }

  // 更新
  if (stateNode && flags & Update) {
    updateNode(stateNode, wip.alternate.props, wip.props);
  }

  // 递归
  commitWorker(wip.child);
  commitWorker(wip.sibling);
}

2.实现Hook (创建 src\react\hooks.ts)

let workInProgressHook:any = null;
// 当前工作的fiber
let currentlyRenderingFiber:IFiber | null = null;

export function renderHooks(wip:any) {
  currentlyRenderingFiber = wip as IFiber;
  currentlyRenderingFiber.memoizedState = null;
  workInProgressHook = null;
}
export function updateWorkInProgressHook():IHook {
  let hook:IHook = {
    memoizedState: null,
    next: null,
  };

  if (!currentlyRenderingFiber) {
    return hook;
  }

  // 拿到老节点
  const current = currentlyRenderingFiber.alternate;
  if (current) {
    // 将老节点的 hook 移到新节点之上
    currentlyRenderingFiber.memoizedState = current.memoizedState;

    if (workInProgressHook) {
      // 否则依次返回下一个
      hook = workInProgressHook = workInProgressHook.next;
    } else {
      // 不存在从第一个开始
      hook = workInProgressHook = currentlyRenderingFiber.memoizedState;
    }
  } else {
    // 初次渲染,初始化hook

    // eslint-disable-next-line no-lonely-if
    if (workInProgressHook) {
      // 存在,直接加载链表的最后面,并且将指针移到当前hook
      workInProgressHook = workInProgressHook.next = hook;
    } else {
      // 不存在,使用当前hook 初始化当前fiber的memoizedState,并且记录当前hook
      workInProgressHook = currentlyRenderingFiber.memoizedState = hook;
    }
  }

  return hook;
}

2.1 useReducer

export function useReducer<T>(reducer:Function, initialState:T) {
  // 按照顺序拿到对应的hook值
  const hook = updateWorkInProgressHook();

  if (!currentlyRenderingFiber?.alternate) {
    // 第一次渲染
    hook.memoizedState = initialState;
  }

  const dispatch = (action:any) => {
    hook.memoizedState = reducer(hook.memoizedState, action);
    scheduleUpdateOnFiber(currentlyRenderingFiber);
  };

  return [hook.memoizedState, dispatch];
}

2.1 useState

export function useState<T>(initialState:T) {
  const hook = updateWorkInProgressHook();

  if (!currentlyRenderingFiber?.alternate) {
    // 第一次渲染
    hook.memoizedState = initialState;
  }

  const dispatch = (state:T) => {
    hook.memoizedState = state;
    scheduleUpdateOnFiber(currentlyRenderingFiber);
  };

  return [hook.memoizedState, dispatch];
}

源码

上一篇 下一篇

猜你喜欢

热点阅读