react中context到底是如何传递的-源码分析
title: react-Context 题外话,个人不喜欢有魔法性的代码,比如context到底如何传递的,为嘛Provider可以直接通过context上下文传递 我的其他React源码分析系列 https://github.com/jimwmg/JiM... https://github.com/jimwmg/React- react中使用context 基本要求就是
1 先看一个组件 class BaseDataSelect extends Component { //只在组件重新加载的时候执行一次 constructor(props) { super(props); //.. } //other methods } //super其实就是下面这个函数 function ReactComponent(props,context,updater) { this.props = props; this.context = context; this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } //自执行函数 var Provider = function (_Component) { _inherits(Provider,_Component); //父组件需要声明 Provider.prototype.getChildContext = function getChildContext() { return { store: this.store }; }; //这里其实就产生了闭包 function Provider(props,context) { _classCallCheck(this,Provider); var _this = _possibleConstructorReturn(this,_Component.call(this,props,context)); //这行代码是我加的测试代码,在控制台输出的就是一个Provider对象 console.log(_this); _this.store = props.store; return _this; } Provider.prototype.render = function render() { return _react.Children.only(this.props.children); }; //父组件需要声明 Provider.childContextTypes = {store:PropTypes.storeShape.isRequired,} return Provider; }(_react.Component); 对,就是我们常用的Provider组件; 实际中的运用(App. 是经过connect过的组件) const store = createStore(reducer) ReactDOM.render( <Provider store={store}> <App /> </Provider>,document.getElementById('root') ); 2 那么传递context的工作是由谁来做的呢?当然是react了; ReacrDOM.render其实就是ReactMount.render函数; 以下是react如何将ReactElement挂载到实际DOM元素上的step过程; ReactMount.js源码地址 var ReactMount = { //nextElement就是ReactELement,jsx语法将组件或者div,span等转化为一个ReactElement对象 //这里就是Provider组件生成的ReactElement对象; //step1 render: function (nextElement,container,callback) { //将ReactElement对象和container元素传递给_renderSubtreeIntoContainer函数; return ReactMount._renderSubtreeIntoContainer(null,nextElement,callback); },//step2 _renderSubtreeIntoContainer: function (parentComponent,callback){ .....//具体源码看上面源码地址 var nextContext; if (parentComponent) { //parentComponent为null ; var parentInst = ReactInstanceMap.get(parentComponent); nextContext = parentInst._processChildContext(parentInst._context); } else { //所以传递下去的nextContext = enmtyObject; nextContext = emptyObject; } //..... var component = ReactMount._renderNewRootComponent(nextWrappedElement,shouldReuseMarkup,nextContext) ._renderedComponent.getPublicInstance(); return component; },//step3 //下面这个函数实现将ReactElement元素,转化为DOM元素并且插入到对应的Container元素中去; _renderNewRootComponent: function (nextElement,context) { //instantiateReactComponent(nextElement,false)函数返回一个组件的实例,该函数源码下面会解释; var componentInstance = instantiateReactComponent(nextElement,false); // The initial render is synchronous but any updates that happen during // rendering,in componentWillMount or componentDidMount,will be batched // according to the current batching strategy. //这个函数是真正的将ReactElement元素插入到DOM元素的,会进入到batchedMountComponentIntoNode函数中; ReactUpdates.batchedUpdates(batchedMountComponentIntoNode,componentInstance,context); var wrapperID = componentInstance._instance.rootID; instancesByReactRootID[wrapperID] = componentInstance; return componentInstance; } } //step 4 //====================会进入到mountComponentIntoNode函数中 function batchedMountComponentIntoNode(componentInstance,context) { var transaction = ReactUpdates.ReactReconcileTransaction.getPooled( /* useCreateElement */ !shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement); transaction.perform(mountComponentIntoNode,null,transaction,context); ReactUpdates.ReactReconcileTransaction.release(transaction); } //step 5 //==================== function mountComponentIntoNode(wrapperInstance,context) { var markerName; if (ReactFeatureFlags.logTopLevelRenders) { var wrappedElement = wrapperInstance._currentElement.props.child; var type = wrappedElement.type; markerName = 'React mount: ' + (typeof type === 'string' ? type : type.displayName || type.name); console.time(markerName); } //markup是经过解析成功的HTML元素,该元素通过_mountImageIntoNode加载到对应的DOM元素上; //注意经过上面的函数层层调用,最后到这里的context还是emptyObject var markup = ReactReconciler.mountComponent(wrapperInstance,ReactDOMContainerInfo(wrapperInstance,container),0 /* parentDebugID */ ); if (markerName) { console.timeEnd(markerName); } wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance; ReactMount._mountImageIntoNode(markup,wrapperInstance,transaction); } //step 6 //_mountImageIntoNode _mountImageIntoNode: function (markup,instance,transaction) { !isValidContainer(container) ? process.env.NODE_ENV !== 'production' ? invariant(false,'mountComponentIntoNode(...): Target container is not valid.') : _prodInvariant('41') : void 0; if (shouldReuseMarkup) { var rootElement = getReactRootElementInContainer(container); if (ReactMarkupChecksum.canReuseMarkup(markup,rootElement)) { ReactDOMComponentTree.precacheNode(instance,rootElement); return; } else { var checksum = rootElement.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME); rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME); var rootMarkup = rootElement.outerHTML; rootElement.setAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME,checksum); var normalizedMarkup = markup; var diffIndex = firstDifferenceIndex(normalizedMarkup,rootMarkup); var difference = ' (client) ' + normalizedMarkup.substring(diffIndex - 20,diffIndex + 20) + 'n (server) ' + rootMarkup.substring(diffIndex - 20,diffIndex + 20); if (transaction.useCreateElement) { while (container.lastChild) { container.removeChild(container.lastChild); } DOMLazyTree.insertTreeBefore(container,markup,null); } else { // 利用innerHTML将markup插入到container这个DOM元素上 setInnerHTML(container,markup); // 将instance(Virtual DOM)保存到container这个DOM元素的firstChild这个原生节点上 ReactDOMComponentTree.precacheNode(instance,container.firstChild); } if (process.env.NODE_ENV !== 'production') { var hostNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild); if (hostNode._debugID !== 0) { ReactInstrumentation.debugTool.onHostOperation({ instanceID: hostNode._debugID,type: 'mount',payload: markup.toString() }); } } } 3 context如何传递的? step2 - step5中开始出现context进行往下传递;这里传递的一直是emptyObject; 主要看下step5中 var markup = ReactReconciler.mountComponent(wrapperInstance,0 /* parentDebugID */ [ReactReconciler.js源码地址. 其实就是执行下面这个函数: mountComponent: function (internalInstance,hostParent,hostContainerInfo,parentDebugID) // 0 in production and for roots { //这里传进去的还是emptyObject; var markup = internalInstance.mountComponent(transaction,parentDebugID); if (internalInstance._currentElement && internalInstance._currentElement.ref != null) { transaction.getReactMountReady().enqueue(attachRefs,internalInstance); } if (process.env.NODE_ENV !== 'production') { if (internalInstance._debugID !== 0) { ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID); } } return markup; }, 对于internalInstance是React组件,而不是宿主DOM元素的情况; ReactCompositeComponent.js源码地址 注意这里internalInstance.mountComponent其实就是ReactCompositeComponent.js中的mountComponent方法; mountComponent: function (transaction,context) { var _this = this; //这里的this指的是internalInstance,也就是经过React处理ReactElement对象之后生成的React组件实例对象; this._context = context; this._mountOrder = nextMountID++; this._hostParent = hostParent; this._hostContainerInfo = hostContainerInfo; //internalInstance._currentElement.props var publicProps = this._currentElement.props; //这里这里是第一次处理context;其实是一个emptyObject;_processContext实现看上面链接,不放了,免得乱; var publicContext = this._processContext(context); //这里Component就是Provider函数; var Component = this._currentElement.type; var updateQueue = transaction.getUpdateQueue(); // Initialize the public class var doConstruct = shouldConstruct(Component); //flag1: 注意这里,这里会真的调用Provider函数,生成 new Provider实例对象 var inst = this._constructComponent(doConstruct,publicProps,publicContext,updateQueue); var renderedElement; // These should be set up in the constructor,but as a convenience for // simpler class abstractions,we set them up after the fact. inst.props = publicProps; inst.context = publicContext; inst.refs = emptyObject; inst.updater = updateQueue; this._instance = inst; // Store a reference from the instance back to the internal representation ReactInstanceMap.set(inst,this); var markup; if (inst.unstable_handleError) { markup = this.performInitialMountWithErrorHandling(renderedElement,context); } else { //flag2 : 这里接着处理子组件 markup = this.performInitialMount(renderedElement,context); } if (inst.componentDidMount) { if (process.env.NODE_ENV !== 'production') { transaction.getReactMountReady().enqueue(function () { measureLifeCyclePerf(function () { return inst.componentDidMount(); },_this._debugID,'componentDidMount'); }); } else { transaction.getReactMountReady().enqueue(inst.componentDidMount,inst); } } return markup; },
这里重点看下 _processChildContext: function (currentContext) { var Component = this._currentElement.type; //这个inst就是Provider组件new之后的实例对象 var inst = this._instance; var childContext; if (inst.getChildContext) { if ("development" !== 'production') { ReactInstrumentation.debugTool.onBeginProcessingChildContext(); try { //这里通过Provider.prototype.getChildContext上得到context值 childContext = inst.getChildContext(); } finally { ReactInstrumentation.debugTool.onEndProcessingChildContext(); } } else { childContext = inst.getChildContext(); } } if (childContext) { return _assign({},currentContext,childContext); } return currentContext; }, (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |