查看视频调试demo_7

context流程图

cursor/valueStack

react源码中存在一个valueStack和valueCursor用来记录context的历史信息和以后context,另外还有一个didPerformWorkStackCursor用来示意以后的context有没有变动

//ReactFiberNewContext.new.jsconst valueCursor: StackCursor<mixed> = createCursor(null);
const didPerformWorkStackCursor: StackCursor<boolean> = createCursor(false);
//ReactFiberStack.new.jsconst valueStack: Array<any> = [];
function pushProvider(providerFiber, nextValue) {  var context = providerFiber.type._context;  {    push(valueCursor, context._currentValue, providerFiber);    context._currentValue = nextValue;  }}
function popProvider(providerFiber) {  var currentValue = valueCursor.current;  pop(valueCursor, providerFiber);  var context = providerFiber.type._context;  {    context._currentValue = currentValue;  }}
  • 在render阶段调用updateContextProvider的时候会执行pushProvider,将新的值push进valueStack中
  • 在commit阶段调用completeWork的时候会执行popProvider,将栈顶context pop进去,

为什么会有这样一个机制呢,因为咱们的context是跨层级的,在之前讲到render阶段和commit阶段的时候,咱们会以深度优先遍历的形式遍历节点,如果波及跨层级读取状态就有点力不从心了,就须要一层一层往下传递咱们的props,所以咱们能够用一个stack记录咱们的context,在render阶段pushProvider,在commit阶段popProvider,在每个具体的层级能依据valueCursor取以后value

createContext

export function createContext<T>(  defaultValue: T,  calculateChangedBits: ?(a: T, b: T) => number,): ReactContext<T> {  if (calculateChangedBits === undefined) {//能够传入计算bit的函数    calculateChangedBits = null;  } else {    //...  }  const context: ReactContext<T> = {    $$typeof: REACT_CONTEXT_TYPE,    _calculateChangedBits: calculateChangedBits,//计算value变动的函数    _currentValue: defaultValue,//dom环境的value    _currentValue2: defaultValue,//art环境的value    _threadCount: 0,    Provider: (null: any),    Consumer: (null: any),  };  context.Provider = {    $$typeof: REACT_PROVIDER_TYPE,    _context: context,  };  if (__DEV__) {  } else {    context.Consumer = context;  }  return context;}//示例const NameChangedBits = 0b01;const AgeChangedBits =  0b10;const AppContext = createContext({}, (prevValue, nextValue) => {  let result = 0;  if (prevValue.name !== nextValue.name) {    result |= NameChangedBits;  };  if (prevValue.age !== nextValue.age) {    result |= AgeChangedBits;  };  return result;});

在简化之后的createContext中能够看到,context和Provider、Consumer的关系是这样的:

context.Provider = {    $$typeof: REACT_PROVIDER_TYPE,    _context: context,  };context.Consumer = context;

useContext

useContext会调用readContext,readContext会创立dependce,退出以后fiber的dependencies链表中

function readContext(context, observedBits) {  {  if (lastContextWithAllBitsObserved === context) ; else if (observedBits === false || observedBits === 0) ; else {    var resolvedObservedBits;    //生成resolvedObservedBits    if (typeof observedBits !== 'number' || observedBits === MAX_SIGNED_31_BIT_INT) {      lastContextWithAllBitsObserved = context;      resolvedObservedBits = MAX_SIGNED_31_BIT_INT;    } else {      resolvedObservedBits = observedBits;    }    var contextItem = {//生成dependce      context: context,      observedBits: resolvedObservedBits,      next: null    };    if (lastContextDependency === null) {      //...      lastContextDependency = contextItem;      currentlyRenderingFiber.dependencies = {//dependencies链表的第一个        lanes: NoLanes,        firstContext: contextItem,        responders: null      };    } else {      lastContextDependency = lastContextDependency.next = contextItem;//退出dependencies链表    }  }  return  context._currentValue ;}

相干参考视频解说:进入学习

provider/customer

在render阶段会调用updateContextProvider,留神几个要害的步骤

  • pushProvider:将以后context退出valueStack
  • calculateChangedBits:useContext能够设置observedBits,没有设置的话就是MAX_SIGNED_31_BIT_INT,也就是31位1,用于计算changedBits,这个计算context是否变动的过程就产生在calculateChangedBits函数中,用这样的形式能够进步context变动之后的性能
  • bailoutOnAlreadyFinishedWork/propagateContextChange:如果changedBits没有扭转则走bailoutOnAlreadyFinishedWork的逻辑,跳过以后节点的更新,如果扭转则执行propagateContextChange
function updateContextProvider(current, workInProgress, renderLanes) {  var providerType = workInProgress.type;  var context = providerType._context;  var newProps = workInProgress.pendingProps;  var oldProps = workInProgress.memoizedProps;  var newValue = newProps.value;  //...  pushProvider(workInProgress, newValue);  if (oldProps !== null) {    var oldValue = oldProps.value;    var changedBits = calculateChangedBits(context, newValue, oldValue);    if (changedBits === 0) {//context没有扭转      if (oldProps.children === newProps.children && !hasContextChanged()) {        return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);      }    } else {//context扭转了      propagateContextChange(workInProgress, context, changedBits, renderLanes);    }  }  var newChildren = newProps.children;  reconcileChildren(current, workInProgress, newChildren, renderLanes);  return workInProgress.child;}
function calculateChangedBits(context, newValue, oldValue) {  if (objectIs(oldValue, newValue)) {        //没有扭转    return 0;  } else {    var changedBits = typeof context._calculateChangedBits === 'function' ? context._calculateChangedBits(oldValue, newValue) : MAX_SIGNED_31_BIT_INT;    {      if ((changedBits & MAX_SIGNED_31_BIT_INT) !== changedBits) {        error('calculateChangedBits: Expected the return value to be a ' + '31-bit integer. Instead received: %s', changedBits);      }    }    return changedBits | 0;  }}//示例const NameChangedBits = 0b01;const AgeChangedBits = 0b10;const AppContext = createContext({}, (prevValue, nextValue) => {  let result = 0;  if (prevValue.name !== nextValue.name) {    result |= NameChangedBits;  };  if (prevValue.age !== nextValue.age) {    result |= AgeChangedBits;  };  return result;});
function propagateContextChange(workInProgress, context, changedBits, renderLanes) {  var fiber = workInProgress.child;  if (fiber !== null) {    fiber.return = workInProgress;//fiber不存在 就找父节点  }  while (fiber !== null) {    var nextFiber = void 0;//遍历fiber    var list = fiber.dependencies;    if (list !== null) {      nextFiber = fiber.child;      var dependency = list.firstContext;      while (dependency !== null) {//遍历dependencies链表        if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) {                    //有变动          if (fiber.tag === ClassComponent) {            //创立新的update            var update = createUpdate(NoTimestamp, pickArbitraryLane(renderLanes));            update.tag = ForceUpdate;             enqueueUpdate(fiber, update);          }          fiber.lanes = mergeLanes(fiber.lanes, renderLanes);//合并优先级          var alternate = fiber.alternate;          if (alternate !== null) {            alternate.lanes = mergeLanes(alternate.lanes, renderLanes);          }          scheduleWorkOnParentPath(fiber.return, renderLanes); //更新先人节点的优先级          list.lanes = mergeLanes(list.lanes, renderLanes);           break;        }        dependency = dependency.next;      }    }         //...      nextFiber = fiber.sibling;    } else {      nextFiber = fiber.child;    }    //...    fiber = nextFiber;  }}

updateContextConsumer要害的代码如下,执行prepareToReadContext判断优先级是否足够退出以后这次render,readContext取到以后context的value

function updateContextConsumer(current, workInProgress, renderLanes) {  var context = workInProgress.type;  //...  prepareToReadContext(workInProgress, renderLanes);  var newValue = readContext(context, newProps.unstable_observedBits);  var newChildren;  {    ReactCurrentOwner$1.current = workInProgress;    setIsRendering(true);    newChildren = render(newValue);    setIsRendering(false);  }    //...  workInProgress.flags |= PerformedWork;  reconcileChildren(current, workInProgress, newChildren, renderLanes);  return workInProgress.child;}