Javascript

react源码解析17.context

本文主要是介绍react源码解析17.context,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
## react源码解析17.context #### 视频课程(高效学习):[进入课程](https://xiaochen1024.com/series/60b1b600712e370039088e24/60b1b636712e370039088e25) #### 课程目录: [1.开篇介绍和面试题](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b311cf10a4003b634719) [2.react的设计理念](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b31ccf10a4003b63471a) [3.react源码架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b328cf10a4003b63471b) [4.源码目录结构和调试](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b32ecf10a4003b63471c) [5.jsx&核心api](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b334cf10a4003b63471d) [6.legacy和concurrent模式入口函数](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b33acf10a4003b63471e) [7.Fiber架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b340cf10a4003b63471f) [8.render阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b348cf10a4003b634720) [9.diff算法](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b354cf10a4003b634721) [10.commit阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b360cf10a4003b634722) [11.生命周期](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b367cf10a4003b634723) [12.状态更新流程](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b36ecf10a4003b634724) [13.hooks源码](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b374cf10a4003b634725) [14.手写hooks](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b37acf10a4003b634726) [15.scheduler&Lane](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b556cf10a4003b634727) [16.concurrent模式](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b55ccf10a4003b634728) [17.context](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b564cf10a4003b634729) [18事件系统](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b56ccf10a4003b63472a) [19.手写迷你版react](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b57bcf10a4003b63472b) [20.总结&第一章的面试题解答](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b581cf10a4003b63472c) [21.demo](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b587cf10a4003b63472d) #### 查看视频调试demo_7 #### context流程图 ![react源码17.1](https://gitee.com/xiaochen1024/assets/raw/master/assets/20210529105951.png) ![react源码17.2](https://gitee.com/xiaochen1024/assets/raw/master/assets/20210529105954.png) #### cursor/valueStack react源码中存在一个valueStack和valueCursor用来记录context的历史信息和当前context,另外还有一个didPerformWorkStackCursor用来表示当前的context有没有变化 ```js //ReactFiberNewContext.new.js const valueCursor: StackCursor = createCursor(null); ``` ```js const didPerformWorkStackCursor: StackCursor = createCursor(false); ``` ```js //ReactFiberStack.new.js const valueStack: Array = []; ``` ```js function pushProvider(providerFiber, nextValue) { var context = providerFiber.type._context; { push(valueCursor, context._currentValue, providerFiber); context._currentValue = nextValue; } } ``` ```js 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 ```js export function createContext( defaultValue: T, calculateChangedBits: ?(a: T, b: T) => number, ): ReactContext { if (calculateChangedBits === undefined) {//可以传入计算bit的函数 calculateChangedBits = null; } else { //... } const context: ReactContext = { $$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的关系是这样的: ```js context.Provider = { $$typeof: REACT_PROVIDER_TYPE, _context: context, }; context.Consumer = context; ``` #### useContext useContext会调用readContext,readContext会创建dependce,加入当前fiber的dependencies链表中 ```js 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 ```js 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; } ``` ```js 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; }); ``` ```js 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 ```js 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的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!