【原创】react-源码解析 - forward-ref&context(4)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【原创】react-源码解析 - forward-ref&context(4)相关的知识,希望对你有一定的参考价值。

参考技术A 通常我们会通过ref去获取Dom节点的实例,或者ClassComponent的实例,但是,如果我么们的组件是一个function类型的component,由于functionComponent是没有实例的所以我们在使用的时候也相应的取不到改组件的this,当然ref也一样。这时react为我们提供了一个forwardRef方法:

          通过这种方式创建的函数类型的组件,使我们能够在函数中继续使用ref属性,当然我们在实际的应用的中也不会傻到去取函数类型组件的ref,因为我们知道它是没有实例的。但是,当我们在使用其他库提供的组件的时候,我们可能并不知道这个这个组件的类型,这时如果能够合理的使用这个方法将会为我们省去不必要的麻烦,同时这里也有HOC的思想在里面。接收一个组件,返回对原组件进行包装的新的组件。接下来我们去看看方法的源码: forwardRef 源码很简单,改方法返回了一个Oject具有render属性,同时$$typeof为"react.forward_ref"的Symbol值。

        这里可能存在对于type属性的概念混淆。我们一定不能认为使用forward创建出的组件的$$typeof属性为:'react.forward_ref'。我们使用forwardRef创建的组建的额时候,实际是将上面例子中的TargetCom作为参数传入到CreateElement方法中的,实际返回的element中的$$typeof还是REACT_ELEMENT_TYPE, 也就是说这里我们将TargetCom创建出的对象--具有render和$$typeof属性传入,其中CreateElement的type属性为forward方法返回的那个对象,也就是说在type对象里面有个叫做$$typeof的属性这个属性的键值为:'react.forward_ref',

在后安的渲染过程中有很多判断,其中有一些就是更具$$typeof展开的,这里我们一定要搞清楚凡是通过CreateElement创建的组件的$$typeof属性都为: 'REACT_ELEMENT_TYPE'。

        这里我们还是按照惯例对api进行一下简单的说明,我们知道在react中是通过props属性来实现组件间通信的,这种通信方式存在的问题在于,虽然父子组件之间通信很方便但是当我们的组件嵌套层级很深,这时候如果使用props传参就不太现实了,首先中间层的组件不一定是你自己写的其次中间层组件声明的props对于这些组件本身没有任何意义,这个时候我们就需要使用context方法帮助我们实现多级组件之间的通信。我们在顶层组件中提供了context对象之后,所有的后代组件都可以访问这个对象。以此达到跨越多层组件传递参数的功能。在react当前版本中有两种实现context的方式:

(1)ParentComponent.childContextTypes == 不推荐,下个大版本会废弃

(2)const Provider, Consumer = React.createContext('default');

在使用childContextTypes时候我们需要在父级组件中声明一个getChildContext的方法,该方法返回一个对象,这个对象就是我们需要传给后代组件的context对象。当我们在使用第一种方法的时候我们需要在组件上声明context对象中属性的类型,有些类似于react的PropTypes类型检测。同时需要在使用到context的后代组件中声明contextTypes类似于下面这种写法:

如果不这样声明的话,在后代组价中是取不到context对象的。这里我们需要注意的是我们在子组件中使用context的时候,需要哪个属性就必须去contextTypes中声明,因为改组件的上级组件不止一个上级组件中的context也不止一个。而createContext方法的使用就简化了很多,首先我们看到改方法返回两个对象Provider, Consumer分别为context的提供方和订阅方:

在上层组件中声明之后,在想用到context的后代组件中国使用Consumer包括起来就可以访问到之前声明的context: ReactContext

从源码中我们可以看到CreateContext方法创建了一个对象改对象有一个_currenValue属性记录context的变化,这个对象Provider属性中声明context,然后使改对象的Consumer属性指向对象本身,我们在使用Consumer的时候就直接从context的currenValue上去取值。以上就是react中的Createcontext方法的实现原理,当然实际过程并没有这么简单,至于具体的实现我们接着往下看。同时这里我们也需要注意该对象下的$$typeof属性并不是用来替换ReactElement中的$$typeof, 与我们之前将到的forwardRef中声明的$$typeof一样都只是我们传入CreateElement方法中type属性上的内容。

了解更多: react-source-code

floodlight之forwarding模块源码解析

以“一个包在交换机上匹配失败,向控制器上发packet_in包,控制器计算出路径,给该路径上所有交换机下发相应流表项”这个过程为例,分析floodlight中forwarding模块源码。

 

说明:floodlight采用事件驱动的异步框架。有三个基本组件module、service和listener,floodlight由许多module组成,每个module实现一个基本功能,同时实现对应的service接口。Service由module实现,向其他module提供服务。Listener作为监听器,采用观察者模式实现,被观察者(module)内部维护一个观察者列表,当事件发生时,被观察者通知列表中注册的观察者,观察者实现listener接口内部的方法,在方法中编写感兴趣的代码。

(需要补充学习java设计模式中的观察者模式,是一个很实用,经常出现的设计模式。)

 

一、Forwarding.java继承关系介绍




(1)Forwarding.java类继承自ForwardingBase类,ForwardingBase是一个抽象类,内部有很多具体方法和部分抽象方法,Forwarding实现了抽象方法,同时调用ForwardingBase中的具体方法实现其他功能。(java设计模式中的模板方法模式)

(2)实现了IfloodlightModule,表示Forwarding类是作为一个独立的模块添加到floodlight中。

(3)实现了IOFSwitchListener,IlinkDiscoveryListener,IroutingDecisionChangedListener接口,表示该module对以上三类事件感兴趣,在本类中需要向相应模块注册并实现接口中相应方法。


二、源码解析


Floodlight采用一个事件驱动机制,因此该模块的触发机制是ForwrdingBase类中的receive方法,当交换机向控制器上发packet_in包时,该方法被执行


Receive方法调用processPacketInMessage方法实现功能。processPacketInMessage分析如下:


processPacketInMessage是一个抽象方法,交由子类Forwarding实现。

子类中的processPacketInMessage方法中根据decision.getRoutingAction()的不同有许多对应的操作,这里我们主要分析类型为FORWARD的操作,如下:


doForwardFlow方法如下:


根据入参获取packet_In包的传入交换机srcSw、端口srcPort、源主机srcDevice和目的主机dstDevice对象。下面的if根据以上参数执行其他才做(如doFlood)。

主要关注该方法下面的:


上图1处调用routing模块向外提供的routingEngineService,获取网络中源设备到目标设备中的路径path。

上图2中根据packet_in包中内容,创建下发的流表项中的匹配域matchField的内容。

分析

上图3中向path路径上所有交换机下发流表。

下面主要分析pushRoute方法(在ForwardingBase中):


上图1处中通过route获取该路径上所有交换机端口对的列表。

上图2处循环中分别获取每个OF交换机对象。

下面接着分析pushRoute:


Fmb对象可以理解为流表构造器。Switch语句中根据flowModCommand的不同分别创建不同类型的构造器,这里下发的流表项可以有add,delete,modify这几种。

接着向下分析该方法的源码:


一个FlowModUtils需要三个参数,fmb流表构造器,actions动作集和需要下发的交换机。

Aob对象是动作集actions的构造器:


通过aob给actions中添加对应的动作。


给fmb对象设置匹配域match,输出端口outPort等一些其他参数。


构造一条流表,包括匹配域和动作集。


通过messageDamper.write(sw,fmb.build());将流表写入消息缓存,后继其他模块会将流表写入对应的交换机中。

下面的,给该路径上所有交换机下发了流表后,还要下发packet_out消息,通知交换机将消息转发出去。通过pushPacket实现。


PushPacket方法也是构造一条packet_out消息,写入messageDamper中下发给交换机。

 


总结:

一个包在交换机上匹配失败后,通过packet_in消息上发给控制器,控制器的ForwardBase模块通过receive方法捕捉到该包后,调用processPacketInMessage对该消息进行处理,该方法调用doForwardFlow实现给从源设备到目的设备路径上交换机下发流表,然后下发packet_out包让上发packet_in包的交换机将第一个packet转发出去。

使用了routing模块对外提供的service,获取从网络中源设备到目的设备之间的路径,封装到path中。

 

 

 

 

 

以上是关于【原创】react-源码解析 - forward-ref&context(4)的主要内容,如果未能解决你的问题,请参考以下文章

floodlight之forwarding模块源码解析

floodlight之forwarding模块源码解析

[C++标准库探索] 解析C++ move forward swap源码 原理探究

[C++标准库探索] 解析C++ move forward swap源码 原理探究

[C++标准库探索] 解析C++ move forward swap源码 原理探究

[原创]spring源码解析之前置知识点