Abstract

This article introduced the concepts of single-page applications and routing, and showed you how to implement single-page applications using two techniques :hash mode and History mode.

1. The content

  • What is a single page app
  • Features of a single page application
  • Implement a single page application with routing
  • Hash pattern
  • The history mode
  • conclusion

2. What is a single page app

Single Page application, SPA for short. It literally means that an app has only one page, which is a very subversive concept and kind of counterintuitive. In our general sense, an app must have multiple pages. For example, a news website must have a home page, a list page, a detail page, etc. We may not be able to imagine an app with only one page. “Just a home page.” “Just a static profile page.”

Definitely not. Let’s spend a little more time looking at history.

Earlier websites are multi-page, that is, literally multi-page: the home page may be index.html, list page is list.html, detail page is detail.html, etc., such advantages are clear development, 3P (ASP, JSP, PHP) technology is a typical representative. With the rise of mobile devices, more and more pages are accessed via mobile devices, and the natural drawbacks of multi-page applications are revealed:

  1. A white screen will inevitably appear when the page jumps. 2. When jumping from page A to page B, it is inevitable that there will be duplicate parts (for example, the header and footer are generally common), and these duplicate parts will obviously cause unnecessary traffic waste. These problems are not a problem on the PC, but they are a big problem on mobile devices. In order to improve user experience, let users see the page faster and save traffic, programmers invented the single-page application technology.

The main feature of a single page application is to use a single page (the name of the page is index.html) to do all the functions (multiple pages).

Actually, SPA, MPA these names are all with the development of technology application and, as the concepts of smartphones and feature phones, the nokia (around 2010), popular in the world they are not being created, how much be the topic of our discussion on the chord, but when a stronger, more intelligent mobile phone, We wanted to differentiate, so we called them feature phones. Similar concepts are server-side rendering and client-side rendering.

3. Features of single page apps

The nature of single-page apps is obvious: changes in the address bar cause content to change, but the entire page does not refresh. Here’s a schematic:

The advantage of this is that once you get the index.html page for the first time, you don’t need to request other pages. All the functionality is done in this one page.

4. Single-page application implementation mode – Routing

Now let’s put aside the term single page app and focus on how to implement specific functions: the content in the address bar changes, the body content is updated, but the entire page is not refreshed.

It’s what we call front-end routing.

What is a route? Not a router.

Routing is a set of rules that determine what content to display on the page body based on the address in the address bar. For example, when the address is /list, the content is the content of the list page. When the address is /detail, the content is the content of the detail page, and so on.

The routing function can be implemented in two ways:

  • Hash pattern. It basically listens for the browser’s Hashchange event.
  • The history mode. It basically calls the pushState,replaceState methods of History and listens for popState events.

Both use browser API features to achieve their goals and, strictly speaking, are not part of THE CONTENT of JS. Let’s take a look at each of these techniques.

5. The hash pattern

Using hash mode to do this is easier than using history mode.

5.1 the principle

When the address bar in the page changes from index.html/#/detail to index.html/#/list:

  • The page does not refresh.
  • The hashChange event is raised. This will be the focus of our code implementation.
  • Appends a browsing record to the browser. This means that you can switch between content using the browser’s forward and back buttons.

5.2 code

<! DOCTYPE html><html lang="zh">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
  <title>Document</title>
  <style>
    body {
      background-color: #eee;
    }
    #app {
      background-color: #fff;
      margin: 2px auto;
      padding: 2em;
    }
    #content{padding:0.5 em}
</style>
</head>
<body>
  <div id="app">
    <nav>
      <a href="# /">The home page</a>
      <a href="#/list">List of pp.</a>
      <a href="#/detail">Details page</a>
    </nav>
    <button id="btn">Skip to details page</button>
    <div id="content">
    </div>
  </div>
  <script>
    let content = null
    // The hashchange event will not be triggered after the page is loaded
    window.addEventListener('DOMContentLoaded'.function(){
      content = document.querySelector("#content")
      onHashChange()
    })
    // Redirect: Directly sets the hash value change
    document.getElementById('btn').addEventListener('click'.() = >{
      location.hash='/detail';
    })
    
    // Listen for route changes
    window.addEventListener('hashchange', onHashChange)
    function onHashChange(e) {
      console.log(e)
      switch (location.hash) {
        case "# /":
          content.innerHTML = "Contents of the home page"
          break;
        case "#/list":
          content.innerHTML = "Contents of a list page"
          break;
        case "#/detail":
          content.innerHTML = "Contents of the Details page"
          break;
        default:
          content.innerHTML = "404"
          break; }}</script>
</body>
</html>
Copy the code

5.4 summary

Hash mode has a strange symbol # in the address bar to make the address look strange.

6. The history mode

6.1 the principle

PushState + popState event

PushState method

PushState (state,title,[URL]) is a method on the history object. What it does is:

(1) Add a state to the history stack of the current browser session, which alters the history;

(2) Change the address bar information;

(3) It does not refresh the page to make the request.

You can now open any browser, write three lines of code in sequence from the console, and hit Enter to run

history.pushState(null.null.'/a')
history.pushState(null.null.'/b')
history.pushState(null.null.'/c')
Copy the code

The renderings are as follows:

Please note the following details:

  1. There is a change in the address bar. From baidu.com/a – > baidu.com/b – > baidu.com/c

  2. The page is not refreshed.

  3. The browser’s forward and back buttons are now available.

However, don’t hit the refresh button, because this will cause the browser to actually request the baidu.com/a page, which doesn’t exist.

ReplaceState method

It works like pushState, except for the replace effect. For example, if you pushState the address bar from a, B, and C, the history is /a->/b->/c, and you can use the forward and back buttons to switch between the three records. However, if replaceState is used, the current access record will be replaced by /a->/b->/d, and the most recent record /c will be replaced by /d. See the following figure for specific effects:

Event – popstate

The POPState event is triggered when the active history entry changes.

window.addEventListener('popstate'.() = >{console.log('popState event',location.pathname)})
Copy the code

We can use the above code to listen for popState events. So under what circumstances can this event be triggered? There are two types of operations:

  1. Click your browser’s forward and back buttons
  2. Call history.back(), history.forward(), history.go() and other methods to manipulate history records.

Note that calling history.pushState() does not trigger the popState event.

If the active history entry was created by a call to history.pushState (), or is affected by a call to history.replacEstate (), the state property of the popState event contains a copy of the state object for the history entry.

Different browsers handle popState events differently when loading a page. Chrome and Safari usually emit a popState event when a page loads, but Firefox does not.

6.2 the target

With that in mind, let’s look at what we are going to achieve. Specific as follows

The effect is that after clicking the A TAB, the address bar changes, and the main content changes with it. Notice that there is no # in the address bar during this process.

6.3 train of thought

This can be accomplished in two parts:

  1. Hijack the jump action of the A tag, in a callback:

    Call pushState, which modifies the address of the address bar (available via location. pathName) to display the content based on the value of location. pathNameCopy the code
  2. Listen for popState events to display the content based on the current pathName when the browser moves forward or backward.

window.addEventListener('popstate'.() = >{display content according to changes in location.pathname})Copy the code

6.4 code

<! DOCTYPE html><html lang="zh">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
  <title>Document</title>
  <style>
    body {
      background-color: #eee;
    }
    #app {
      background-color: #fff;
      margin: 2px auto;
      padding: 2em;
    }
    #content{padding:0.5 em}
</style>
</head>
<body>
  <div id="app">
    <nav>
      <a href="/">The home page</a>
      <a href="/list">List of pp.</a>
      <a href="/detail">Details page</a>
    </nav>
    <button id="btn">Skip to details page</button>
    <div id="content">
    </div>
  </div>
  <script>
    let content = null
    window.addEventListener('DOMContentLoaded', onLoad)
    // Jump to the page directly
    document.getElementById('btn').addEventListener('click'.() = >{
      history.pushState(null.null.'/detail')
      onPopStateChange()
    })
    function onLoad() {
      // Update the page content
      content = document.querySelector("#content")
      onPopStateChange()
            
      // Intercept the A tag
      var aLinks = document.querySelectorAll('a[href]')
      aLinks.forEach((a,idx) = > {
        a.addEventListener('click'.e= > {
          e.preventDefault()
          history.pushState(null.null, e.target.getAttribute('href'))
          onPopStateChange()
        })
      })
    }
    
    // Listen for popState changes
    window.addEventListener('popstate', onPopStateChange)
        // Implement specific routing functions: switch the page content according to the address
    function onPopStateChange(e) {
      console.log(e,Date.now())
      switch (location.pathname) {
        case "/":
          content.innerHTML = "Here's what the home page says."
          break;
        case "/list":
          content.innerHTML = "Here's the contents of the list page."
          break;
        case "/detail":
          content.innerHTML = "Here's what the details page says."
          break;
        default:
          content.innerHTML =  'You visited${location.pathname}There is no `
          break; }}</script>
</body>
</html>
Copy the code

6.5 summary

In history mode, the address appears normal without # in the address bar. It uses some of H5’s more advanced APIS (pushState), so be aware of browser compatibility issues, and it may not be able to refresh the page, because when refreshing, the browser will actually request the page information, and it may not be available on the server!

7. To summarize

Single-page apps were born in the era of mobile Internet. Their goal is not to refresh the entire page, but to determine what content is displayed in the content area through changes in the address bar. To achieve this, we will use front-end routing technology, specifically in two ways: hash mode and history mode. Hash mode is implemented by listening for the hashChange event, and History mode is implemented by the pushState method + popState event.

Reprinted from mp.weixin.qq.com/s/yTq-yZYAk…