The front-end routing

The concept of routing originally comes from the server side, where routing describes the mapping between urls and processing functions.

In Single Page Application (SPA), routing describes the mapping between URL and UI. This mapping is one-way, that is, THE CHANGE of URL causes UI update (no Page refresh is required).

Traditional Routing Mode

Each time the page jumps, the backend server returns a new HTML document. Take the following example

An HTML document

<! -- Home page -->
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
  <ul>
    <li><a href='/home'>home</a></li>
    <li><a href='/about'>about</a></li>

    <div id="routeView">
        Home
    </div>
  </ul>
</body>
</html>


<! -- About page -->
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
  <ul>
    <li><a href='/home'>home</a></li>
    <li><a href='/about'>about</a></li>

    <div id="routeView">
        About
    </div>
  </ul>
</body>
</html>
Copy the code

Server Configuration

Nodejs, KOA framework is used here

const Koa = require("koa");
const Router = require("koa-router");
const path = require("path");
const views = require("koa-views");
let app = new Koa();
let router = new Router();
//home.html and about.html are placed under the views file
app.use(views(path.join(__dirname, "views/"), { extension: "html" }));
// Route matching rule
router.get("/home", index);
router.get("/", index);
router.get("/about", about);

// The corresponding render function
async function index(ctx) {
  await ctx.render("home");
}
async function about(ctx) {
  await ctx.render("about");
}
app.use(router.routes());
app.listen(3333);
Copy the code

The above is the traditional route jump implementation, which requires the cooperation of the server side. Each time the path changes, the back end will match and return a new HTML document after success, so it is also called multi-page application.

Front-end routing implementation

Different from traditional routing, front-end routing is not controlled by the server and does not refresh the page when the URL changes, but still renders the same HTML file, so it is also called SPA application. (Single Page Application)

  • Key points of front-end routing implementation
    • How can I change the URL without causing a page refresh?
    • How do I detect URL changes?

The two core questions above are answered using hash and history implementations, respectively.

Hash pattern

  • Hash is the hash (#) and subsequent part of the URL that is used as an anchor to navigate within the page. Changing the hash part of the URL does not cause a page refresh
  • Listen for URL changes through the Hashchange event, and there are only a few ways to change a URL: by moving the browser forward or backward, by passing<a>The hashChange event is triggered when the tag changes the URL, or when the URL is changed via window.location

HTML part

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
  <ul>
    <! -- Define route -->
    <li><a href="#/home">home</a></li>
    <li><a href="#/about">about</a></li>

    <! Render route UI -->
    <div id="routeView"></div>
  </ul>
</body>
</html>
Copy the code

Js part

// The hashchange event will not be triggered after the page is loaded
window.addEventListener('DOMContentLoaded', onLoad)
// Listen for route changes
window.addEventListener('hashchange', onHashChange)

// Routing view
let routerView = null

function onLoad () {
  routerView = document.querySelector('#routeView')
  onHashChange()
}


// When the route changes, render the corresponding UI according to the route
function onHashChange () {
  switch (location.hash) {
    case '#/home':
      routerView.innerHTML = 'Home'
      break
    case '#/about':
      routerView.innerHTML = 'About'
      break
    default:
      break}}Copy the code

As you can see from the above example, it is possible to render view content dynamically with JS by listening for changes in the hash (URL) of the page.

The history mode

This type is implemented through HTML5’s latest History API.

  • History provides pushState and replaceState methods, which change the path portion of the URL without causing a page refresh
  • History provides a PopState event similar to the Hashchange event, but the PopState event is a little different: a POPState event is triggered when a URL is changed forward or backward by the browser, via pushState/replaceState or<a>Tag URL changes do not trigger popState events. Fortunately, we can intercept the pushState/replaceState call and<a>Tag click events to detect URL changes, so listening for URL changes can be implemented

HTML part

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
  <ul>
    <li><a href='/history/home'>home</a></li>
    <li><a href='/history/about'>about</a></li>

    <div id="routeView"></div>
  </ul>
</body>

</html>
Copy the code

Js:

window.addEventListener('DOMContentLoaded', onLoad)
// Listen for route changes
window.addEventListener('popstate', onPopState)

// Routing view
let routerView = null

function onLoad () {
  routerView = document.querySelector('#routeView')
  onPopState()

  // Intercepts the default behavior of  tag click events, using pushState to modify the URL and update the manual UI, thus implementing the effect of clicking links to update the URL and UI.
  const linkList = document.querySelectorAll('a[href]')
  linkList.forEach(el= > el.addEventListener('click'.function (e) {
    e.preventDefault()
    history.pushState(null.' ', el.getAttribute('href'))
    onPopState()
  }))
}

// When the route changes, render the corresponding UI according to the route
function onPopState () {
  switch (location.pathname) {
    case '/history/home':
      routerView.innerHTML = 'Home'
      return
    case '/history/about':
      routerView.innerHTML = 'About'
      return
    default:
      return}}Copy the code

It’s the same as a normal URL, but it also has the disadvantage that when you refresh the page, the page will be lost,

The solution needs to be configured on the server side because the server is requested to refresh the URL, but there is no resource on the server, so 404 is reported

const Koa = require("koa");
const Router = require("koa-router");
const path = require("path");
const views = require("koa-views");
let app = new Koa();
let router = new Router();
app.use(views(path.join(__dirname, "views/"), { extension: "html" }));
http://localhost:8888/history / / correct matching, rendering the HTML
router.get("/history", index)

async function index(ctx) {
  await ctx.render("history");
}

app.use(router.routes());
/ / if there is no match Such as http://localhost:8888/history/home, then returned to the initial page (first page)
app.use(async (ctx,next)=>{
    await next();
    if(parseInt(ctx.status)==404){
        ctx.response.redirect("/history")
    }
})

app.listen(8888);

Copy the code

summary

The foregoing describes the principles and implementation methods of front-end routing in hash mode and histroy, which are native and do not depend on any framework. Next time we will briefly explore the implementation principle of vue-Router