Pre-knowledge: the browser records history and jumps

Most of you are familiar with this part, but here’s a quick summary to remind you:

  1. PushState (replaceState) and Location.hash;
  2. The difference between pushState and replaceState is that pushState adds a historical record while replaceState replaces the current record.
  3. The first parameter of pushState(replaceState) is the state to be passed inhistory.stateIn order to get. Will be used internallyStructured copy algorithmSerialized storage, the copied results will be recorded in the history stack records;
  4. If the current is not at the top of the history stack, pushState again, and all subsequent records of the current history will be overwritten.
  5. Window.history. go can be used to move forward and backward with numeric arguments. Note:history.go(0), its meaning is to refresh the current page, and the behavior is the same as that of the location.reload method. History. go only controls the movement of the stack pointer within the stack.
  6. When setting location.hash, be aware that if you set the location.hash value to the same hash value as the browser URL address, no events will be triggered and no history will be added. Or if the same value is set to location.hash twice before and after, only the first location.hash setting takes effect, and the second setting of the same value does not generate any events or history records.
  7. If you want to change the hash in the address bar without pushing the hash, pass thewindow.location.replaceImplementation (but refreshes the page when the absolute path is set);
  8. It is triggered by clicking the Browser’s “Back” or “Forward” button, invoking the Go, back, or Forward methods of History, or changing the hash of some browserspopstateEvent, but not with history.pushState or history.replaceState
  9. hashchangeEvents can be triggered by setting location.hash, manually modifying the hash in the address bar, calling window.history.go, or clicking the “Forward” or “Back” button in the browser.
  10. Note that window.history.pushState does not trigger the hashchange event, even if the urls navigated back and forth differ only in part from the hash;
  11. Can be achieved bywindow.dispatchEvent(new PopStateEvent('popstate'))To manually trigger the popState event; The same thing can be done bywindow.dispatchEvent(new HashChangeEvent('hashchange'))To manually trigger the Hashchange event.

The history library

The History library is an important dependency library of the React Router. It plays an important role as the navigator and listener. It provides three types of history objects: BrowserHistory (browserHistory object), hashHistory (hashHistory object) and memoryHistory (memoryHistory object).

The History library provides create factory functions for three classes of history objects: createBrowserHistory, createHashHistory, and createMemoryHistory.

Each of the above factory functions can be passed into a configuration object to configure the behavior of the corresponding history object. There is a getUserConfirmation jump confirmation function.

The getUserConfirmation jump confirmation function is usually used to alert the user when a navigation is generated before the end of the process. This function is used in conjunction with history.back. History. block accepts no arguments, either a string, Boolean argument, or a prompt function. When history.block is called, any navigation including browser forward or backward behavior is blocked, or the user is prompted in some way to confirm the navigation.

When prompt is set to string, prompt is displayed by default, which is window.confirm. If getUserConfirmation is passed in when creating the history, use the prompt passed in.

browserHistory

BrowserHistory, also known as the browserHistory object, is fully compatible with the properties of the window.location object in the browser. You can pass in the following configuration:

interface BrowserHistory { basename? : string; forceRefresh? : boolean; getUserConfirmation? :typeofgetUserConfirmation; keyLength? : number; }Copy the code

The analysis is as follows:

  1. Basename: the base path. The createHref, history.push, and history.replace methods are used to concatenate basename and path.
  2. ForceRefresh: For browserHistory, the default jump will not cause a page refresh. If forceRefresh is set to true, the page will be forcibly refreshed during the jump.
  3. GetUserConfirmation: jump confirmation function;
  4. KeyLength: Each push call generates a random key that can be retrieved from BrowserHistory. location and persisted in window.history.state. The length of this random string is controlled by the keyLength configuration when history is created. By default, it is 6 and serves to identify the navigation.

browserHistory.push

Browserhistory. push calls history.pushState at the bottom of the browserHistory.push method, but it differs from the native pushState in several ways:

  1. The first argument, in addition to being a string address, can also be passed as a path description object, as shown below:
browserHistory.push({
    pathname:'/index'.search:'? id=1'.hash:'#test'.state:{pid=11}})Copy the code
  1. Unlike the original, it can be found inbackThe method is called and changes its behavior as follows:
history.back()
history.push('/about') // After calling the back() method, the push call has no effect

history.back('Sure to jump to about? ')
history.push('/about') // The system default popup window will pop up when you call push. Click "Confirm" to jump to the page

Copy the code
  1. PushState does not trigger a popState event, whereas push does after a state updatehistory.listenListen to the callback function, callback function parameters for the current latest address object and the value of “PUSH” navigation behavior identification;

Note: When forceRefresh is true for creating history, the framework will not use the pushState native method, but will call window.location.href=href directly to refresh the page

browserHistory.replace

The browserHistory.replace method calls history.replaceState underneath, and since replaceState does not trigger a popState event, the replace call does not trigger a popState event either. Also note that if forceRefresh is true, the page will be updated using window.location.replace(href) to force a refresh of the page.

browserHistory.listen

Browserhistory. listen can listen for methods like hisotry.push, history.replace, and history. At the same time, listening for location “change” does not mean that the value of location is inconsistent during the navigation process. If the value of location is consistent during the navigation process, the callback function of history.listen will also be triggered.

History is designed to return an unsubscribe function after a call to the subscribe function, which can be called to unlisten history

hashHistory

You can use hashHistory when the browser does not support native interfaces such as pushState, or if you do not want to refresh the page during page switching and want to store the page URL in the hash of the browser address.

When creating a hashHistory, you can pass in configuration objects like this:

interface HashHistory { basename? :string; hashType? : HasnType; getUserConfirmation? :typeof getUserConfirmation;
}
Copy the code

The other parameters have been covered above, but the emphasis here is on hashType. HashType supports hashbang, noslash, and slash. if hashType is not specified when creating a hashType, the default hashType is slash.

  1. Hashbang: Hash path # followed by! With “/;
  2. Noslash: corresponding hash path “#” without “/”;
  3. Slash: corresponding hash path “#” followed by “/”;

Note: When creating a hashHistory, the path changes even when there is no operation, such as hashType=slash, where the path is automatically marked with #/, because the hashType is initialized by calling replaceHashPath(‘/’).

hashHistory.push

The hashHistory.push method calls location.hash underneath, and its argument can accept either a string path or a description object for location, as shown below:

hashHistory.push({
    pathname:'/index'.search:'? id=1'.hash:'#test'.state:{pid=11}})Copy the code

HashHistory will route the hash portion of the browser address, and each call to push will only change the hash value.

Just like browserHistory, hashHistory.push is changed by the back method, as shown in the above example.

Here are a few things to note:

  1. If a string is passed in the push method## # # # # # # # # # # # # # # # # # # # # # # # # # # # #
location.href = 'http://www.xxx.com/#/index'
hashHistory.push('/about#id')
console.log(window.location.href) // http://www.xxx.com/#/about#id
console.log(hashHistory.location.hash) // #id
console.log(window.location.hash) // #/about#id

Copy the code
  1. If hashhistory. push is set to state, older versions of the history library will warn that it is not supported. For compatibility, it is not recommended to set state directly to the second parameter. Can be set inside the location object state, but only in hashHistory. Location. Read in the state, not in the window. The history. Read in the state, and does not have the capacity to persistent state.

The hashhistory. replace and hashhistory. go methods are basically the same as browerHistory. –

history.createHref

The createHref method converts the location object into the corresponding URL string, and creates the href value for the A tag inside the Link component.

There are two things to note when using hashhistory.createhref:

  1. CreateHref does nothing to codec the original string;
  2. CreateHref determines whether there is a base element in the HREF attribute in the HTML document stream.

memoryHistory

MemoryHistory is usually run outside of the browser and is usually used for testing or as a ReactNative environment. When created, you can pass in the following object configuration:

interface MemoryHistory { getUserConfirmation? :typeofgetUserConfirmation; initialEntries? : string[]; initialIndex? : number; keyLength? : number; }Copy the code

The analysis is as follows:

  1. initialEntriesA history stack similar to the Browser Router or Hash Router that initializes the stack contents. Default is [‘/’];
  2. initialIndexRepresents the initial stack pointer position, 0 by default;

MemoryHistory, in addition to the generic history attribute, has index, entries, and canGo attributes:

  1. Index is the current position of the historical stack pointer;
  2. Entries are historical stack array;
  3. CanGo is used to determine whether the jump position n can be jumped;

memoryHistory.push

A call to memoryHistory.push allows location to be stored in memory and accessed via memoryHistory.location or entries.

Like browserHistory and hashHistory, memoryHistory supports not only calling blocks to block jumps, but also relative path navigation, saving state, and so on, but its state is maintained by itself. It does not exist in window.history.state for persistent storage.

The history library principle

Run the process

Whatever the form of history, the internal flow is universal. First, History maintains a callback array to store the functions that listen listens to. When methods push,replace, etc., are called, the back method is checked. If it is called and the input parameter is not false, it is checked before navigation. There are two cases: 1. If the parameter is a string, the string content is used to prompt the user to decide whether to perform the navigation operation. 2. If the parameter is false, the navigation is cancelled without prompting. When the navigation is successful, History collects all changes internally and updates them to the properties of History. When history’s status is updated, the listen callback is triggered. Note that since memoryHistory does not run in a browser, it does not natively listen for events and block navigation.

History. The back analysis

History. The back is how to intercept the navigation, the key is the internal call transitionManager confirmTransitionTo, its main function is to confirm that whether should stop jumping.

The key to confirmTransitionTo is to set the prompt function: first determine whether prompt is null, and if not, intercept. But if you use the go method or click forward and back in the navigation bar, how to achieve interception? Essentially, when you click forward and back, the address has changed, this time you need to use manual recovery.

The idea of manual recovery isto calculate the distance delta needed to recover from simulated memory stacks (such as browserHistory allKeys), the address that has been jumped, and the address that you want to recover. Finally, go(delta) is used to jump the address.

reference

  • Learn more about React Router: from principle to implementation