I. Introduction to History

The History object contains the URL that the user has visited (in a browser window), which is part of the Window object and is accessible through the window.history property. The History object is crucial in front-end applications, and all single-page applications are routed based on the History object.

Second, the introduction

This article will briefly introduce some properties of the history object, and then focus on some practical applications of the history object to help deepen our understanding of the history object.

Three, attribute introduction

The image above shows the History object I printed on the console. Let’s take a quick look at these properties.

3.1 the property value

  • Length: Returns the number of urls in the browser history list.
  • ScrollRestoration: The scrollRestoration property allows a Web application to explicitly set the default scrollRestoration behavior on the history navigation. This property has two optional values, defaults to Auto, and restores the position on the page to which the user has scrolled. The other value is: manual, where the position on the page is not restored and the user must manually scroll to that position.
  • State: Returns a value that represents the state at the top of the history stack, which is a way to view the state without waiting for a POPState event.

3.2 methods

  • The history.pushState(object, title, URL) method takes three arguments: Object is an object saved with the state, title is the title of the new page, and URL is the new URL.
  • The only difference between replaceState(object, title, URL) and pushState is that it replaces the top element of the history stack.
  • History.go (x) goes to the corresponding URL history.
  • History.back () is the browser’s equivalent of the back button.
  • History.forward () is the browser’s equivalent of the forward button.

3.3 event

  • Popstate events: PopState events are triggered when:

Triggered when the browsing history of the same document changes. Calls to the history.pushState() and history.replacEstate () methods do not trigger. Triggered when the user clicks the browser’s forward/back buttons, or when the back(), forward(), and go() methods of the history object are called. The popState event callback takes an event object whose state property is the object stored with the state.

3.4 understand

3.4.1 track problem

Introducing the history object, let’s start with a few questions:

1. Is the history object mutable? 2. History.length represents the number of urls in the browser’s history list. Can this number be infinite? 3. What is the difference between location.href and history.pushState? 4. If I jump from domain A to domain B, where does history.back() go? 5. What is the trigger condition for the POPState event?

3.4.2 answer

Let’s answer these questions in turn to get a better understanding of the history object.

Question 1

Is the history object mutable?

explore

We assign history to an empty object and then print history, which is not an empty object.

conclusion

The window.history object is immutable

Question 2

History. length represents the number of urls in the browser’s history list. Can this number be infinite?

explore

We first print history.length and find that it is 3; Then we add 100 records, print history.length again, and find that the value is 50.

conclusion

History. length is not infinite

Question 3

What’s the difference between location.href and history.pushState?

explore

[image.png- a52EE3-1609847856284-0]

Let’s take Baidu H5 page as an example. First, let’s enter HTTP:www.baidu.com, and print the history object with length 2. Next we use location.href = ‘www.zhihu.com’ and found that the page jumped to Zhihu. At this time, we printed history again and found that the length changed to 3. At this point, we click the browser back, return to baidu H5 page again, print the history, still 3.PushState (null, “”,www.zhihu.com’), found to throw an error, meaning that pushState cannot be used to jump between different domains. We then use history.pushState(null, “, /a “) and notice that a’ /a’ path has been added to the page URL, but look at the console and see that no further requests have been sent to the server. Use location.href = ‘/a’ again and see that the browser has issued a document request and the page is Not Found

conclusion

1. After the location.href jump, the page will initiate a new document request, but history.pushState will not. 2. Location. Href can jump to other domain names, but history can’t. 3. Location. href and history both add a record to the history list.

Question 4

If I jump from domain A to domain B, where does history.back() go?

explore

Or take Baidu H5 page as an exampleWe use location.href = ‘www.zhihu.com’ to jump Then, using the history.back() method, the page goes back to the www.baidu.com page

conclusion

If you jump from domain A to domain B, calling history.back() will return to domain A

Question 5

What is the trigger condition for the PopState event?

explore

First we listen for the popState event, and then I call location.href, location.hash, history.go, history.back, history.forward, history.pushState, The history.replaceState method yields the following results

conclusion

1. Location. Href is a refresh jump, so the printable message will not be printed. In a similar way to location.href, history.go(0) will also refresh the page directly, so this listener will also be disabled and no information will be printed. 2. The popState event is triggered by location.hash, as is history. Back, history. Either history.pushState or history.replaceState will not trigger the popState event.

Four, the application

Now that we’ve had our first look at the History object, let’s look at some of its practical applications

4.1 Single-page Application

PushState allows you to change the path of the address bar without refreshing the page, so you only need to request HTML once when you enter the page for the first time, and js controls subsequent page rendering. Load different JS modules according to different URL paths. The important thing about using the history route is that the server needs to be prepared to handle urls, because when a user refreshes a page with the URL ‘/a/b/ C ‘, the server will probably return a 404 status code because there is no path match, and should return HTML files for all such paths as well.

4.2 Interactive Operations

The problem

Another common class is interaction implementation classes. For example, the following interaction: 1. After creating/editing the page, the user makes changes to the form, and gives a second popup confirmation when exiting. 2. On the list page of the mobile terminal, clicking the filter box will pop up a floating layer. When the user clicks the back button of the APP, the floating layer will be closed instead of going back to the page. 3. If the user is currently on page A, click to go to page B. The user is found to have no permission in page B, and the user is redirected to error page C. If the user does not click the back button of the browser on page C, the user can return to page B again.

answer
Analysis of the

1. Interaction 1 is the same problem as interaction 2. The principle is that clicking the forward and back buttons of the browser will trigger a POPState event. Note that when the popState event is triggered, the historical address record is already rolled back. There is nothing we can do to prevent the rollback, so before the rollback, We need to use history.pushState(null,null,document.URL) to actively add another record to the current URL. When popState is triggered, the URL does not change, although a record is backlogged. It also achieves the purpose of staying on the current page. For interaction 3, use the history.replace method. If we were to use pushState or location.href all the time, the history would look like A — B — C, However, if we use history.replace when we jump from B to C, B record will be replaced by C record, and the history record will be changed to A — C. At this time, click the back button from PAGE C to directly return to page A.

The instance

Below I give a click on the browser button after the popup window effect, for your reference. Take Baidu H5 page for example. On the ‘/ A ‘page, when I click return, a pop-up window banning return will pop up.

The specific code is as follows and can be used on the console

history.pushState(null, null, '/a') window.addEventListener('popstate', () => {alert(' return ')}) history.pushState(null, null, document.url)Copy the code

4.3 Basis of various routing frameworks

Routing frameworks typically have three modes: BrowserHistory, hashHistory, and memoryHistory, where browserHistory is implemented depending on the window.history object, let’s think about two problems first, and then implement a simple front-end single-page route.

The problem

1. What is the difference between using window.history.pushState and routing framework pushState? 2. Since history.pushState does not trigger popState events, how does the routing framework load different components when pushState is used? 3. Why does a history object have a key in its state after pushState?

answer

Let’s analyze these problems.

The experiment

First, we dig the homepage, click on the frontend, and find that when entering the ‘/frontend’ path, there is no HTML request sent, indicating that this is a single page application. Let’s go back to the homepage. Use history.pushState(null, null, ‘/frontend’) to enter the front section and see what happens.As you can see, the URL has changed, but the page has not rendered the front-end module.Taking a look at the vue-Router source, we can see that it calls a pushState function. Let’s look at this functionI don’t see anything special about pushState, which is a call to history.pushState. The vue-router calls the history.pushState method when it uses the push function. The vue-Router adds a key to the history.pushState function when it uses the push function.We can see that the value of this key is a time, does that have any special meaning? A later review of official documents produced this explanation:When a history jumps through an application's push or replace, it can store "location state" in the new location instead of showing it in the URL, just like form data posted in an HTML. In the DOM API, these hash histories are easily used to jump through window.location.hash = newHash without storing their location state. But we want the entire history to be able to use location state, so we create a unique key for each location and store their state in the Session storage. When visitors click "Back" and "forward," we have a mechanism to restore those location states.Back to q1 and Q2, since pushState is nothing special, let’s look at transitionTo again.I found this code that calls the route’s callback function. As you know, we usually register a route in this formrouter.route('/111', state => { contentDOM.innerHTML = '111'; });So this is executionstate => { contentDOM.innerHTML = '111'; }PushState not only calls history.pushState, but also calls the route’s corresponding callback to render the corresponding component.

conclusion

So we conclude that the pushState of the routing framework is not the same as history.pushState. The pushState of the routing framework not only calls history.pushState to change the URL, but also destroys the old component according to the URL. Render the new component; The key in state is compatible with hashHistory.

4.4 Front-end Route Demo

Now that we have an HTML demo, we need to write a Router for it to achieve the following effect:

<! DOCTYPE HTML > < HTML lang="en"> <head> <meta charset="UTF-8"> <title> cursor: pointer; } .link:hover { text-decoration: underline; } </style> </head> <body> <ul> <li><a class="link" data-href="/A">A</a></li> <li><a class="link" data-href="/B">B</a></li> <li><a class="link" data-href="/C">C</a></li> <li><a class="link" data-href="/D">D</a></li> </ul> <div id="wrapper"></div> <script> // create instance const router = new router (); const contentDOM = document.querySelector('#wrapper'); Router. route('/A', state => {contentdom.innerhtml = 'A'; }); router.route('/B', state => { contentDOM.innerHTML = 'B'; }); router.route('/C', state => { contentDOM.innerHTML = 'C'; }); router.route('/D', state => { contentDOM.innerHTML = 'D'; }); </script> </body> </html>Copy the code

A simple analysis is as follows: 1. Publish and subscribe mode must be first. When registering routes, the corresponding callback function of each route needs to be stored, and the corresponding callback function is executed when the route changes. 2. It is not enough to just listen for popSate. You need to perform the corresponding callback function to actively update the component during page initialization and pushState. 3. There is also the problem of blocking the default events of these A tags. This simple Router is not difficult to implement. The complete code is shown below:

<! DOCTYPE HTML > < HTML lang="en"> <head> <meta charset="UTF-8"> <title> cursor: pointer; } .link:hover { text-decoration: underline; } </style> <script> const noop = () => undefined; class Router { constructor() { this.init(); } // init() {this.routes = {}; this.doListen(); this.makeLink(); {} / / monitor doListen () the window. The addEventListener (' DOMContentLoaded 'this. ListenEventInstance. Bind (this)); window.addEventListener('popstate', this.listenEventInstance.bind(this)); ListenEventInstance () {this.callbackCenter(window.location.pathName); // listenEventInstance() {this.callbackCenter(window.location.pathName); }; Route (pathname, callback = noop) {this.routes[pathname] = callback; } // callbackCenter(pathname) {if (! this.routes[pathname]) { return; } const {state} = window.history; this.routes[pathname](state); } / / bind a label, prevent the default behavior makeLink () {document. AddEventListener (' click ', e = > {const {target} = e; const {nodeName, dataset: {href}} = target; if (! (nodeName === 'A') || ! href) { return; } e.preventDefault(); window.history.pushState(null, '', href); this.callbackCenter(href); }); } } </script> </head> <body> <ul> <li><a class="link" data-href="/A">A</a></li> <li><a class="link" data-href="/B">B</a></li> <li><a class="link" data-href="/C">C</a></li> <li><a class="link" data-href="/D">D</a></li> </ul> <div id="wrapper"></div> <script> // create instance const router = new router (); const contentDOM = document.querySelector('#wrapper'); Router. route('/A', state => {contentdom.innerhtml = 'A'; }); router.route('/B', state => { contentDOM.innerHTML = 'B'; }); router.route('/C', state => { contentDOM.innerHTML = 'C'; }); router.route('/D', state => { contentDOM.innerHTML = 'D'; }); </script> </body> </html>Copy the code

Five, the summary

This article first introduces the various properties of the history object, and then introduces some of its applications, I hope this article can be helpful to you in practical work. In addition to window.history, there are many other knowledge points and related applications in front-end routing. For location objects, building multi-page applications and other knowledge, you can delve further if you are interested.

Six, reference

  • Jqhtml.com: Single-page application deployment solution
  • Nuggets: Performance & Integration — History API
  • The react to the router: the react – the router documentation
  • Vue: vue source code
  • MDN: history object