最前面
这里先说明白,这个ReactCurrentOwner实际上是在虚拟DOM中识别自定义组件的关键。
当我们执行了自定义组件的render,返回的虚拟DOM树里面的节点,不算文本节点、注释节点这类,它就是完全有ReactDomComponent节点组成的,这个ReactCurrentOwner.owner变量,保存了一个瞬时变量,用于存到自定义组件生成的ReactDomComponent节点的_owner
变量上。
作用
ReactCurrentOwner这个主要还是和自定义组件有关系。
使用grep -rn 'ReactCurrentOwner.current =' ./src
全局查了以下。赋值操作主要是在
ReactMultiChild.js
和ReactCompositeComponent.js
文件中。
它有三种赋值情景:
ReactCurrentOwner.current = this._currentElement._owner
ReactCurrentOwner.current = this
ReactCurrentOwner.current = null
情景1
其中,情景一this._currentElement._owner
仅在开发环节会执行
情景2
大部分情况下,一课虚拟DOM树大致由一个TopLevelWrapper组件作为根元素,然后子节点里面ReactCompositeComponent和ReactDomComponent互相交织嵌套,最后以ReactDomComponent为最末节点的结构组成的(这里假装不知道文本在React里面也算节点。。。)。
我们写的业务代码、包含各种生命周期的组件,基本都是ReactCompositeComponent,
ReactCurrentOwner.current也都是指向了这个类型的组件。
在ReactCompositeComponent组件里面存在生命周期,state,props,context这些东西,也存在更新时候的Diff算法优化,如果一个ReactDomComponent组件确定要更新,这里是替换式无脑更新的,不存在Diff算法什么的。
这种情况下,ReactCurrentOwner.current 是指当前正处于构建过程中的组件。
这个变量实际上相当于是一个存在于React作用域全局的一个缓存变量。
ReactCompositeComponent构建时候,ReactCompositeComponent.js里面有一段代码
1 | _renderValidatedComponent: function() { |
他会在render&&update时候执行。
然后就是如果从ReactCompositeComponent到ReactDomComponent这个过程中(实质上这两个类型的组件在虚拟DOM上也就是一个变量的区别)的时候,
1 | var ReactElement = function(type, key, ref, self, source, owner, props) { |
这个current会被缓存到Element对象中。其他时候就是对这个current做null赋值处理了。
分析
接下来就可以思考一下它在React体系中的作用了。`
1 | _renderValidatedComponent: function() { |
_renderValidatedComponent
是一个很重要的函数。但是他实质上却超级简单。。。。
他就做了两件事:
- _renderValidatedComponentWithoutOwnerOrContext——说白了就是组件的render函数调用
- 设置ReactCurrentOwner.current为当前这个组件实例
另外,从ReactCompositeComponent到ReactDomComponent这个过程中的时候,参考一下ReactElement函数的定义:
1 | var ReactElement = function(type, key, ref, self, source, owner, props) { |
这个current会被缓存到Element对象中。其他时候就是对这个current做null赋值处理了。
这里归总一下:
初始render
初始化render时候会执行batchedUpdates,里面下级调用包括ReatReconciler.mountComponent
,这里看看ReactCompositeComponent.mountComponent的调用。
1 | ReactCompositeComponent.mountComponent |
这里自定义组件render执行时候会不断触发其下级自定义组件的render,他们在结构上是嵌套的。
1 | Component.render(SubComponent1.render(SubComponent2.render())) |
关于这个逻辑,可以参考react生命周期-jsx到js的转换。
他们的render是这样一个逻辑,我们想要执行Component.render实际上会先执行SubComponent1.render,进而要先执行SubComponent2.render。
通过这样的调用,我们最先执行的SubComponent2.render会先将SubComponent2赋值到ReactCurrentOwner.current,然后挂载到其下一级(孙节点和以下都不加)ReactDomComponent实例上。
以上逻辑到了SubComponent1和Component都同样进行类推。这样,所有自定义组件render完毕之后,就是一个干净的(就是一个owner变量的区别)由ReactDomComponent组成的虚拟DOM树(文本节点、注释节点等先忽视)。
而由自定义节点直接生成的ReactDomComponent节点,会有一个非null的_owner
属性,指向自定义组件实例。
更新
看更新还是很有必要看看React Diff算法。
看到Component Diff这一节,就基本上可以大致有数了。这里_updateRenderedComponent
函数会组件更新时候必然会调用到ReactCompositeComponent._renderValidatedComponent
。
其他逻辑可以完全参考初始render。
总结
ReactCurrentOwner.current
为什么重要?
以为它是自定义节点的指针。所有的ReactCompositeComponent最终render之后都变成了干干净净的ReactDomComponent节点组成的DOM树,但是如何分辨哪些是ReactCompositeComponent生成的呢?
这就依赖这些ReactDomComponent节点上的owner
变量。
而ReactCurrentOwner.current
正是维护这个在构建虚拟DOM过程中,随时会变动的变量的临时保存位置所在。