最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react源码解析17.context

    正文概述 掘金(全栈潇晨)   2021-06-23   357

    视频课程(高效学习):[进入课程]

    (xiaochen1024.com/series/60b1…)

    context流程图

    react源码解析17.context

    react源码解析17.context

    cursor/valueStack

    react源码中存在一个valueStack和valueCursor用来记录context的历史信息和当前context,另外还有一个didPerformWorkStackCursor用来表示当前的context有没有变化

    //ReactFiberNewContext.new.js
    const valueCursor: StackCursor<mixed> = createCursor(null);
    
    const didPerformWorkStackCursor: StackCursor<boolean> = createCursor(false);
    
    //ReactFiberStack.new.js
    const 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;
    }
    

    下载网 » react源码解析17.context

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元