This article introduces the content of the react router in the core of the history of the Library of detailed analysis, there is a certain reference value, the need for a friend can refer to, I hope you have some help.
Objective
Using React to develop a slightly more complex application, React router is almost the only option for routing management. Although react router experienced 4 large versions of the update, the function is more and more rich, but no matter how changed, its core dependence on the history library has remained unchanged. Let's take a look at what features this library of 4k+ stars on GitHub actually provides.
HTML5 History Object
Talk to the history library, do you think this word is a bit familiar? Yes, the HTML5 specification also adds a new history object with the same name. Let's look at what this history object is used to solve.
In the era of jquery's reign, it was a popular way of page processing to request a page without refresh through Ajax, and the prototype of the SPA evolved at that time. In order to mark the changes in the page, it is convenient to display the correct page elements after the refresh, and the page is uniquely positioned by changing the hash value of the URL. But this poses another problem: users cannot use forward/backward to switch pages.
In order to solve this problem, the history object was born. When the URL or hash of the page changes, the browser will automatically push the new URL into the history object. A state array is maintained inside the history object to record the changes to the URL. When the browser makes a forward/backward operation, it actually invokes the corresponding method (/) of the History object and forward
back
takes out the corresponding state to switch the page.
In addition to manipulating the Url,history object, it also provides 2 ways to update the internal state without manipulating the URL, respectively pushState
replaceState
. Additional data can also be stored in the state and then onpopstate
taken out of the event event.state
. If you want to have a deeper understanding of the history object, you can refer here, and here.
Relationship between the history library and the HTML5 history object
We'll look back at the history library. It essentially does the following 4 things:
Based on the concept of HTML5 history object, some functions have been expanded.
Provides 3 types of history:browserhistory,hashhistory,memoryhistory, and maintains a unified API
Supports the Publish/subscribe function, which can automatically trigger the subscription when history changes
Provides useful functions such as jump intercept, jump confirmation and basename.
Compare the similarities and differences between the two APIs. The following are the history libraries:
Const HISTORY = { length, //property, number of state recorded in historical action, //property, action type location for current navigation , / /attribute, location object, encapsulates pathname, search and hash properties push, //method, navigates to new route, and records replace in history , //method, Replace the current record in the history of the routing information go, //method, forward or backward N records GoBack, //method, back GoForward, //method, forward CanGo, //method, can forward or backward n record block, //method, before jumping let the user determine whether to jump listen //method, subscribe to the history Change event };
The following are the HTML5 history objects:
Const HISTORY = { length, //property, the number of state recorded in histories state , //Properties, Pushstate, and Replacestate objects passed in back, //method, fallback forward, //method, Forward go, //method, forward or backward N records pushstate, //method, Navigate to the new route and record the replacestate //method in the history, replacing the routing information in the history with the current record}//subscribe to the historical Change event Window.onpopstate = function (event) { ...}
As can be seen from the comparison, the relationship between the two is very close, the history library can be said to be a superset of the history object, is a more powerful history object.
Createhashhistory Source Code Analysis
Below, we take one of three types of history, Hashhistory, as an example, to analyze the source of the history of history, to see what it has done. Let's look at how it handles the hash change.
Construct Hashhistory Object Const Createhashhistory = (props = {}) + = {... const globalhistory = window.history; Referencing the HTML5 History object ...//Transitionmanager is responsible for controlling whether to jump, and the subscribers to be notified after the jump, the following will be discussed in detail const Transitionmanager = Createtra Nsitionmanager (); ...//register subscriber of the history change callback Const LISTEN = Listener = {Const Unlisten = Transitionmanager.appendlistener (li Stener); Checkdomlisteners (1); return () = {checkdomlisteners (-1); Unlisten (); }; }; Monitor Hashchange Event Const Checkdomlisteners = Delta = {Listenercount + = Delta; if (Listenercount = = = 1) {Window.addeventlistener (hashchangeevent, Handlehashchange); } else if (listenercount = = = 0) {Window.removeeventlistener (hashchangeevent, Handlehashchange); } }; Hashchange Event Callback Const Handlehashchange = () = {...//the location object used inside the construct, including properties such as pathname, search, and hash Const LOCATIOn = getdomlocation (); .. handlepop (location); }; Handle hash Change Logic Const HANDLEPOP = location + {... const action = "POP"; Show the user the information to confirm the jump (if any) and notify the Subscriber after confirmation. If the user cancels the jump, it rolls back to the previous state Transitionmanager.confirmtransitionto (location, action, getuserconfirmation, OK + = { if (OK) {setState ({action, location}); Notify Subscribers after confirmation} else {Revertpop (location); Cancel to fall back to the previous State}}); }; Update the Action,location and length properties and notify subscribers of const SetState = Nextstate = {object.assign (history, Nextstate); History.length = Globalhistory.length; Transitionmanager.notifylisteners (History.location, history.action); }; ...}
The above is to deal with the logic of the passive hash change, a short summary is: The subscription hash change event, to determine whether it is true to change, if necessary to update their own properties, notify Subscribers, do not need to change back to the previous state.
below to see what Transitionmanager did, focus on publish/subscribe related content, ignore the user confirm jump related content.
Const Createtransitionmanager = () = { ... List of internally maintained subscribers let listeners = []; Registered Subscriber Const Appendlistener = fn + = {Let isActive = true; Const LISTENER = (... args) = = { if (isActive) fn (... args); }; Listeners.push (listener); return () = { isActive = false; Listeners = Listeners.filter (item = Item!== listener);}; } ; Notifies the subscriber const Notifylisteners = (... args) = { Listeners.foreach (listener = Listener (... args)); }; ...}
The code at a glance here is to maintain a list of subscribers and notify the relevant function when the hash is changed.
The above is the change of the hash when the passive update related content, and then see the active update related code, push
as an example, replace
similar.
Const PUSH = (path, state) = = { ... Const ACTION = "PUSH"; Const LOCATION = createlocation (path, undefined, undefined, history.location); Transitionmanager.confirmtransitionto (location, action, getuserconfirmation, OK = = {if (!ok) //If canceled, does not jump return; ... Pushhashpath (Encodedpath); Replace the URL with a new hash ... SetState ({action, location}); Update the Action,location and length properties and notify Subscribers });};/ /Replace with new hash to URL const Pushhashpath = path + (Window.location.hash = path);
The history library is actually implemented by manipulating the HTML5 history object while the browser is in the forward and backward operation.
Const Globalhistory = Window.history;const Go = n + = { ... Globalhistory.go (n);}; Const GOBACK = () + Go ( -1); const GoForward = () = () + go (1);
When called window.history.go
, the hash changes to trigger the Hashchange event, and the history library then notifies the relevant subscriber of the change.
Summarize
This paper gives a more in-depth introduction to the react router core-dependent history library. From the new History object of HTML5, this paper contrasts its relationship with the history Library, and analyzes the implementation details of the code in detail with Hashhistory as an example.
Finally, let's review what the History Library has done:
Based on the concept of HTML5 history object, some functions have been expanded.
Provides 3 types of history:browserhistory,hashhistory,memoryhistory, and maintains a unified API
Supports the Publish/subscribe function, which can automatically trigger the subscription when history changes
Provides useful functions such as jump intercept, jump confirmation and basename.
Although the history Library is a core reliance of react router, it has no dependency on react itself. If you have a scene in your project that has a history of operations, you can also bring it into the project.