摘要:每個(gè)消息將通過一個(gè)的線程進(jìn)行處理,并執(zhí)行與所有模塊的消息相關(guān)聯(lián)的所有邏輯其他模塊也可以注冊類似交換機(jī)連接或斷開和端口狀態(tài)通知特定時(shí)間。默認(rèn)情況下,使用地址和來識別設(shè)備。設(shè)備管理器將了解其他屬性,如地址。在消息轉(zhuǎn)發(fā)實(shí)現(xiàn)前,模塊將啟動。
FloodlightProvider
處理交換機(jī)之間的連接并將 OpenFlow 的消息轉(zhuǎn)化成其他模塊可以監(jiān)聽的時(shí)間
決定某些特定的 OpenFLow 消息(即 PacketIn,F(xiàn)lowRemove,PortStatus 等)被分派到該偵聽消息的模塊的順序,模塊可以決定允許該消息進(jìn)入下一個(gè)監(jiān)聽對象或者停止處理消息
FloodlightProvider 如何工作?FloodlightProvider 使用 Netty 庫來處理到交換機(jī)的線程和連接。
每個(gè) OpenFlow 消息將通過一個(gè) Netty 的線程進(jìn)行處理,并執(zhí)行與所有模塊的消息相關(guān)聯(lián)的所有邏輯
其他模塊也可以注冊類似交換機(jī)連接或斷開和端口狀態(tài)通知特定時(shí)間。
為了使模塊注冊為基于 OpenFlow 消息的,必須實(shí)現(xiàn) IOFMessageListener 接口
要監(jiān)聽 OpenFlow 消息,要先向 FloodlightProvider 注冊
調(diào)用 IFloodlightProviderService(具體由 Controller 類實(shí)現(xiàn))的 addOFMessageListener 方法進(jìn)行注冊訂閱
核心工作是在 ListenerDispatcher 類來完成。
每次增加觀察者都會判斷是否是終結(jié)點(diǎn)(也就是不被其他的 Listener 所依賴),因?yàn)樽罱K確定這些觀察者順序的時(shí)候就是由這些終結(jié)點(diǎn)開始往前進(jìn)行 DFS 遍歷得到
@Override public synchronized void addOFMessageListener(OFType type, IOFMessageListener listener) { //先判斷與type對應(yīng)的 ListenerDispatcher對象是否存在 ListenerDispatcherListenerDispatcher 維護(hù)這些觀察者,有依賴關(guān)系ldd = messageListeners.get(type); if (ldd == null) { ldd = new ListenerDispatcher (); messageListeners.put(type, ldd); } //注冊監(jiān)聽type這個(gè)消息; ldd.addListener(type, listener); }
volatile Listlisteners = new ArrayList ();
//每個(gè)OF msg都有唯一的ListenerDispatcher對象,觀察者存在listeners鏈表中
private boolean ispre(U type, T l1, T l2) { return (l2.isCallbackOrderingPrereq(type, l1.getName()) || l1.isCallbackOrderingPostreq(type, l2.getName())); }
返回兩個(gè)傳入的監(jiān)聽器的順序
public void addListener(U type, T listener) { Listnewlisteners = new ArrayList (); if (listeners != null) newlisteners.addAll(listeners); newlisteners.add(listener); // Find nodes without outgoing edges // 查找沒有出邊的節(jié)點(diǎn) List terminals = new ArrayList (); for (T i : newlisteners) { boolean isterm = true; for (T j : newlisteners) { if (ispre(type, i, j)) { //兩個(gè)都不關(guān)心前后順序的時(shí)候 isterm = false; break; } } if (isterm) { //關(guān)乎有前后順序的監(jiān)聽模塊存入 terminals.add(i); } } if (terminals.size() == 0) { logger.error("No listener dependency solution: " + "No listeners without incoming dependencies"); listeners = newlisteners; return; } // visit depth-first traversing in the opposite order from // the dependencies. Note we will not generally detect cycles /** * 以相反順序訪問深度優(yōu)先遍歷依賴。 注意我們通常不會檢測周期 */ HashSet visited = new HashSet (); List ordering = new ArrayList (); for (T term : terminals) { //進(jìn)行排序 visit(newlisteners, type, visited, ordering, term); } listeners = ordering; }
private void visit(List監(jiān)聽器具有的方法newlisteners, U type, HashSet visited, List ordering, T listener) { if (!visited.contains(listener)) { visited.add(listener); for (T i : newlisteners) { if (ispre(type, i, listener)) { visit(newlisteners, type, visited, ordering, i); } } ordering.add(listener); // } }
public interface IListener查看繼承了 IOFMessageListener 的Type Hierarchypublic enum Command { CONTINUE, STOP } 狀態(tài)值,用來判斷是否繼續(xù)執(zhí)行 public String getName(); //用來判斷 name 的這個(gè)模塊是否要在當(dāng)前對象之前執(zhí)行 public boolean isCallbackOrderingPrereq(T type, String name); //用來判斷 name 的這個(gè)模塊是否要在當(dāng)前對象之后執(zhí)行 public boolean isCallbackOrderingPostreq(T type, String name); IOFMessageListener接口繼承了 IListener 接口,同時(shí)定義了 receive 方法 public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx); 返回 CONTINUE 或者 STOP,繼續(xù)看每個(gè)繼承這個(gè)接口的模塊的重寫
TopologyManager 模塊的IOFMessageListener 重寫的方法:
@Override public String getName() { return MODULE_NAME; //此處為 topology,每個(gè)模塊都有自己的 MODULE_NAME } @Override public boolean isCallbackOrderingPrereq(OFType type, String name) { //從此處可以看出,在執(zhí)行這個(gè)模塊之前,需要先執(zhí)行 MODULE_NAME 為 linkiscovery 的模塊 return "linkdiscovery".equals(name); } @Override public boolean isCallbackOrderingPostreq(OFType type, String name) { return false; } @Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: ctrIncoming.increment();//計(jì)數(shù)器,加一 //調(diào)用這里的執(zhí)行方法 return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; }
通過 Type Hierarchy 可以找到Packet-In消息處理順序的幾個(gè)模塊
FloodlightContextStore 代表的是一種緩存模型(利用的是ConcurrentHashMap,線程安全的 HashMap)
里面存儲的是上下文相關(guān)的對象,能夠根據(jù)相應(yīng)的key得到具體的 Object
存在的意義是Floodlight中注冊監(jiān)聽某個(gè)事件的listener可以在被調(diào)用的時(shí)候直接從中取出上下文信息(context information)
基本數(shù)據(jù)結(jié)構(gòu),這是一個(gè)上下文對象,F(xiàn)loodlight代碼監(jiān)聽器可以注冊它,稍后可以檢索與事件相關(guān)聯(lián)的上下文信息
public class FloodlightContext { protected ConcurrentHashMapstorage = new ConcurrentHashMap (); public ConcurrentHashMap getStorage() { return storage; } }
創(chuàng)建了一個(gè) HashMap storage,
public class FloodlightContextStore{ @SuppressWarnings("unchecked") public V get(FloodlightContext bc, String key) { return (V)bc.storage.get(key); } public void put(FloodlightContext bc, String key, V value) { bc.storage.put(key, value); } public void remove(FloodlightContext bc, String key) { bc.storage.remove(key); } }
一個(gè)FloodlightContextStore對象,可用于PACKET-IN有效內(nèi)容,消息對象是Ethernet類型
public static final FloodlightContextStoreLinkDiscoveryManager 模塊bcStore = new FloodlightContextStore ();
鏈接發(fā)現(xiàn)服務(wù)負(fù)責(zé)發(fā)現(xiàn)和維護(hù) OpenFlow 網(wǎng)絡(luò)中的網(wǎng)絡(luò)連接的狀態(tài)
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: ctrIncoming.increment(); return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; }
主要使用了 handlePacketIn()方法
protected Command handlePacketIn(DatapathId sw, OFPacketIn pi, FloodlightContext cntx) { //提取 Packet-In 的有效分組內(nèi)容 Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); if (eth.getPayload() instanceof BSN) { BSN bsn = (BSN) eth.getPayload(); if (bsn == null) return Command.STOP; if (bsn.getPayload() == null) return Command.STOP; // It could be a packet other than BSN LLDP, therefore // continue with the regular processing. // 它可以是除BSN LLDP之外的分組,因此繼續(xù)進(jìn)行常規(guī)處理。 if (bsn.getPayload() instanceof LLDP == false) return Command.CONTINUE; return handleLldp((LLDP) bsn.getPayload(), sw, inPort, false, cntx); } else if (eth.getPayload() instanceof LLDP) { return handleLldp((LLDP) eth.getPayload(), sw, inPort, true, cntx); } else if (eth.getEtherType().getValue() < 1536 && eth.getEtherType().getValue() >= 17) { long destMac = eth.getDestinationMACAddress().getLong(); if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) { ctrLinkLocalDrops.increment(); if (log.isTraceEnabled()) { log.trace("Ignoring packet addressed to 802.1D/Q " + "reserved address."); } return Command.STOP; } } else if (eth.getEtherType().getValue() < 17) { log.error("Received invalid ethertype of {}.", eth.getEtherType()); return Command.STOP; } if (ignorePacketInFromSource(eth.getSourceMACAddress())) { ctrIgnoreSrcMacDrops.increment(); return Command.STOP; } // If packet-in is from a quarantine port, stop processing. NodePortTuple npt = new NodePortTuple(sw, inPort); if (quarantineQueue.contains(npt)) { ctrQuarantineDrops.increment(); return Command.STOP; } return Command.CONTINUE; }TopolopyManager
為控制器維護(hù)拓?fù)湫畔ⅲ约霸诰W(wǎng)絡(luò)中尋找路由
IOFMessageListener 的 receive 方法 @Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: ctrIncoming.increment(); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; } 主要使用了processPacketInMessage()方法 protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { // get the packet-in switch. Ethernet eth = IFloodlightProviderService.bcStore. get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); if (eth.getPayload() instanceof BSN) { BSN bsn = (BSN) eth.getPayload(); if (bsn == null) return Command.STOP; if (bsn.getPayload() == null) return Command.STOP; // 可能不是 BSN LLDP,繼續(xù)常規(guī)處理 if (bsn.getPayload() instanceof LLDP == false) return Command.CONTINUE; doFloodBDDP(sw.getId(), pi, cntx); return Command.STOP; } else { return dropFilter(sw.getId(), pi, cntx); } }DeviceManagerImpl
DeviceManager基于在網(wǎng)絡(luò)中看到的MAC地址創(chuàng)建設(shè)備
它跟蹤映射到設(shè)備的任何網(wǎng)絡(luò)地址及其在網(wǎng)絡(luò)中的位置
設(shè)備管理器通過 PACKET-IN 消息請求了解設(shè)備,通過 PACKET-IN 消息獲取信息,根據(jù)實(shí)體如何建立進(jìn)行分類。默認(rèn)情況下,entity classifies 使用 MAC 地址和 VLAN 來識別設(shè)備。這兩個(gè)屬性定義一個(gè)獨(dú)一無二的設(shè)備。設(shè)備管理器將了解其他屬性,如 IP 地址。
信息中的一個(gè)重要的部分是設(shè)備的連接點(diǎn),如果一個(gè)交換機(jī)接受到一個(gè) PACKET-IN 消息,則交換機(jī)將會創(chuàng)建一個(gè)連接點(diǎn),設(shè)備也會根據(jù)時(shí)間清空連接點(diǎn),IP 地址,以及設(shè)備本身,最近看到的時(shí)間戳是用來保持清空過程的控制
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: cntIncoming.increment(); return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: break; } return Command.CONTINUE; }
主要使用了processPacketInMessage()方法
protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)); // Extract source entity information Entity srcEntity = getSourceEntityFromPacket(eth, sw.getId(), inPort); if (srcEntity == null) { cntInvalidSource.increment(); return Command.STOP; } // Learn from ARP packet for special VRRP settings. // In VRRP settings, the source MAC address and sender MAC // addresses can be different. In such cases, we need to learn // the IP to MAC mapping of the VRRP IP address. The source // entity will not have that information. Hence, a separate call // to learn devices in such cases. learnDeviceFromArpResponseData(eth, sw.getId(), inPort); // Learn/lookup device information Device srcDevice = learnDeviceByEntity(srcEntity); if (srcDevice == null) { cntNoSource.increment(); return Command.STOP; } // Store the source device in the context fcStore.put(cntx, CONTEXT_SRC_DEVICE, srcDevice); // Find the device matching the destination from the entity // classes of the source. if (eth.getDestinationMACAddress().getLong() == 0) { cntInvalidDest.increment(); return Command.STOP; } Entity dstEntity = getDestEntityFromPacket(eth); Device dstDevice = null; if (dstEntity != null) { dstDevice = findDestByEntity(srcDevice.getEntityClass(), dstEntity); if (dstDevice != null) fcStore.put(cntx, CONTEXT_DST_DEVICE, dstDevice); else cntNoDest.increment(); } else { cntNoDest.increment(); } if (logger.isTraceEnabled()) { logger.trace("Received PI: {} on switch {}, port {} *** eth={}" + " *** srcDev={} *** dstDev={} *** ", new Object[] { pi, sw.getId().toString(), inPort, eth, srcDevice, dstDevice }); } snoopDHCPClientName(eth, srcDevice); return Command.CONTINUE; }vitualNetworkFilter
虛擬網(wǎng)絡(luò)過濾器模塊是基于虛擬化網(wǎng)絡(luò)的數(shù)據(jù)鏈路層,它允許你在獨(dú)立的數(shù)據(jù)鏈路層上創(chuàng)建多個(gè)邏輯鏈路
若是使用 floodlightdefault.properties 則沒有這個(gè)模塊
如何工作在 Floodlight 啟動時(shí),沒有虛擬網(wǎng)絡(luò)創(chuàng)建,這時(shí)主機(jī)之間不能相互通信。
一旦用戶創(chuàng)建虛擬網(wǎng)絡(luò),則主機(jī)就能夠被添加。
在 PACKET-IN 消息轉(zhuǎn)發(fā)實(shí)現(xiàn)前,模塊將啟動。
一旦,一條 PACKET-IN 消息被接受,模塊將查看源 MAC 地址和目的 MAC 地址,如果2個(gè) MAC 地址是同一個(gè)虛擬網(wǎng)絡(luò),模塊將返回 Command.CONINUE消息,并且繼續(xù)處理流。如果MAC 地址不在同一個(gè)虛擬網(wǎng)絡(luò)則返回 Command.STOP 消息,并丟棄包
必須在同一個(gè)物理數(shù)據(jù)鏈路層中
每個(gè)虛擬網(wǎng)絡(luò)只能擁有一個(gè)網(wǎng)關(guān)()【一個(gè)網(wǎng)關(guān)可被多個(gè)虛擬網(wǎng)絡(luò)共享】
多播和廣播沒有被隔離
允許所有的 DHCP 路徑
配置該模塊可用于 OpenStack 的部署
包含此模塊的默認(rèn)配置文件位置:
src/main/resources/neutron.properties
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: return processPacketIn(sw, (OFPacketIn)msg, cntx); default: break; } log.warn("Received unexpected message {}", msg); return Command.CONTINUE; }
主要使用了processPacketIn()方法
protected Command processPacketIn(IOFSwitch sw, OFPacketIn msg, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); Command ret = Command.STOP; String srcNetwork = macToGuid.get(eth.getSourceMACAddress()); // If the host is on an unknown network we deny it. // We make exceptions for ARP and DHCP. if (eth.isBroadcast() || eth.isMulticast() || isDefaultGateway(eth) || isDhcpPacket(eth)) { ret = Command.CONTINUE; } else if (srcNetwork == null) { log.trace("Blocking traffic from host {} because it is not attached to any network.", eth.getSourceMACAddress().toString()); ret = Command.STOP; } else if (oneSameNetwork(eth.getSourceMACAddress(), eth.getDestinationMACAddress())) { // if they are on the same network continue ret = Command.CONTINUE; } if (log.isTraceEnabled()) log.trace("Results for flow between {} and {} is {}", new Object[] {eth.getSourceMACAddress(), eth.getDestinationMACAddress(), ret}); /* * TODO - figure out how to still detect gateways while using * drop mods if (ret == Command.STOP) { if (!(eth.getPayload() instanceof ARP)) doDropFlow(sw, msg, cntx); } */ return ret; }LoadBalancer
IOFMessageListener 的 receive 方法
@Override public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: return processPacketIn(sw, (OFPacketIn)msg, cntx); default: break; } log.warn("Received unexpected message {}", msg); return Command.CONTINUE; }
主要使用了processPacketIn()方法
private net.floodlightcontroller.core.IListener.Command processPacketIn(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); IPacket pkt = eth.getPayload(); if (eth.isBroadcast() || eth.isMulticast()) { // handle ARP for VIP if (pkt instanceof ARP) { // retrieve arp to determine target IP address ARP arpRequest = (ARP) eth.getPayload(); IPv4Address targetProtocolAddress = arpRequest.getTargetProtocolAddress(); if (vipIpToId.containsKey(targetProtocolAddress.getInt())) { String vipId = vipIpToId.get(targetProtocolAddress.getInt()); vipProxyArpReply(sw, pi, cntx, vipId); return Command.STOP; } } } else { // currently only load balance IPv4 packets - no-op for other traffic if (pkt instanceof IPv4) { IPv4 ip_pkt = (IPv4) pkt; // If match Vip and port, check pool and choose member int destIpAddress = ip_pkt.getDestinationAddress().getInt(); if (vipIpToId.containsKey(destIpAddress)){ IPClient client = new IPClient(); client.ipAddress = ip_pkt.getSourceAddress(); client.nw_proto = ip_pkt.getProtocol(); if (ip_pkt.getPayload() instanceof TCP) { TCP tcp_pkt = (TCP) ip_pkt.getPayload(); client.srcPort = tcp_pkt.getSourcePort(); client.targetPort = tcp_pkt.getDestinationPort(); } if (ip_pkt.getPayload() instanceof UDP) { UDP udp_pkt = (UDP) ip_pkt.getPayload(); client.srcPort = udp_pkt.getSourcePort(); client.targetPort = udp_pkt.getDestinationPort(); } if (ip_pkt.getPayload() instanceof ICMP) { client.srcPort = TransportPort.of(8); client.targetPort = TransportPort.of(0); } LBVip vip = vips.get(vipIpToId.get(destIpAddress)); if (vip == null) // fix dereference violations return Command.CONTINUE; LBPool pool = pools.get(vip.pickPool(client)); if (pool == null) // fix dereference violations return Command.CONTINUE; LBMember member = members.get(pool.pickMember(client)); if(member == null) //fix dereference violations return Command.CONTINUE; // for chosen member, check device manager and find and push routes, in both directions pushBidirectionalVipRoutes(sw, pi, cntx, client, member); // packet out based on table rule pushPacket(pkt, sw, pi.getBufferId(), (pi.getVersion().compareTo(OFVersion.OF_12) < 0) ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT), OFPort.TABLE, cntx, true); return Command.STOP; } } } // bypass non-load-balanced traffic for normal processing (forwarding) return Command.CONTINUE; }ForwardingBase
IOFMessageListener 的 receive 方法
@Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: IRoutingDecision decision = null; if (cntx != null) { decision = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION); } return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx); default: break; } return Command.CONTINUE; }
主要使用了processPacketInMessage()方法
public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx);
所有繼承了 ForwardingBase 的子類Forwarding重寫了這個(gè)方法,實(shí)現(xiàn)具體的操作
@Override public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); // We found a routing decision (i.e. Firewall is enabled... it"s the only thing that makes RoutingDecisions) if (decision != null) { if (log.isTraceEnabled()) { log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi); } switch(decision.getRoutingAction()) { case NONE: // don"t do anything return Command.CONTINUE; case FORWARD_OR_FLOOD: case FORWARD: doForwardFlow(sw, pi, decision, cntx, false); return Command.CONTINUE; case MULTICAST: // treat as broadcast doFlood(sw, pi, decision, cntx); return Command.CONTINUE; case DROP: doDropFlow(sw, pi, decision, cntx); return Command.CONTINUE; default: log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction()); return Command.CONTINUE; } } else { // No routing decision was found. Forward to destination or flood if bcast or mcast. if (log.isTraceEnabled()) { log.trace("No decision was made for PacketIn={}, forwarding", pi); } if (eth.isBroadcast() || eth.isMulticast()) { doFlood(sw, pi, decision, cntx); } else { doForwardFlow(sw, pi, decision, cntx, false); } } return Command.CONTINUE; }PACKET-IN
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76540.html
摘要:每個(gè)具體的模塊都會重寫這幾個(gè)函數(shù),下面舉個(gè)的例子。獲得的服務(wù)返回服務(wù)實(shí)現(xiàn)類和實(shí)現(xiàn)用的對象的。服務(wù)是指繼承了接口的類。模塊使用方法可以獲得對應(yīng)的服務(wù)列表,可以到源碼去看對應(yīng)的服務(wù)功能。 Floodlight 的 Main 解析圖 showImg(https://segmentfault.com/img/remote/1460000015816841?w=2048&h=2341); 需要...
摘要:在學(xué)習(xí)源碼的過程中,給我?guī)椭畲蟮木褪沁@個(gè)系列文章,于是決定基于這個(gè)系列文章談一下自己的理解。到此為止,首次渲染就完成啦總結(jié)從啟動到元素渲染到頁面,并不像看起來這么簡單,中間經(jīng)歷了復(fù)雜的層級調(diào)用。 前言 React 是一個(gè)十分龐大的庫,由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級非常深,閱讀其源碼是一個(gè)非常艱辛的過...
摘要:依賴注入和控制反轉(zhuǎn),這兩個(gè)詞經(jīng)常一起出現(xiàn)。一句話表述他們之間的關(guān)系依賴注入是控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式。而兩者有大量的代碼都是可以共享的,這就是依賴注入的使用場景了。下一步就是創(chuàng)建具體的依賴內(nèi)容,然后注入到需要的地方這里的等于這個(gè)對象。 前言 React 是一個(gè)十分龐大的庫,由于要同時(shí)考慮 ReactDom 和 ReactNative ,還有服務(wù)器渲染等,導(dǎo)致其代碼抽象化程度很高,嵌套層級...
前言 本文所有內(nèi)容全部發(fā)布再個(gè)人博客主頁 https://github.com/muwoo/blogs歡迎訂閱。不過最近因?yàn)槭虑楸容^多,有一段時(shí)間沒有更新了,后面打算繼續(xù)不斷學(xué)習(xí)更新,歡迎小伙伴一起溝通交流~ 最近更新 前端單測的那些事 基于virtual dom 的canvas渲染 js Event loop 機(jī)制簡介 axios 核心源碼實(shí)現(xiàn)原理 JS 數(shù)據(jù)類型、賦值、深拷貝和淺拷貝 j...
前言 本文所有內(nèi)容全部發(fā)布再個(gè)人博客主頁 https://github.com/muwoo/blogs歡迎訂閱。不過最近因?yàn)槭虑楸容^多,有一段時(shí)間沒有更新了,后面打算繼續(xù)不斷學(xué)習(xí)更新,歡迎小伙伴一起溝通交流~ 最近更新 前端單測的那些事 基于virtual dom 的canvas渲染 js Event loop 機(jī)制簡介 axios 核心源碼實(shí)現(xiàn)原理 JS 數(shù)據(jù)類型、賦值、深拷貝和淺拷貝 j...
閱讀 1662·2021-09-26 09:55
閱讀 5278·2021-09-22 15:40
閱讀 2022·2019-08-30 15:53
閱讀 1505·2019-08-30 11:15
閱讀 1723·2019-08-29 15:41
閱讀 1878·2019-08-28 18:13
閱讀 3154·2019-08-26 12:00
閱讀 1678·2019-08-26 10:30