preface

When someone asks you what front-end routing is, what do you say?

First of all, when you get a new, big project, which structure of the project do you start with? The basic structure of a project can be seen from the design of the route, so it is very important, although we may not need to change it very often during development, but we still need to understand it.


We all know that in a large project, routing links multiple pages, and all we see is a change in THE URL address. Maybe we can say that the url change caused the page to be updated (re-rendered), or you have a better understanding…

The following article summarizes some knowledge points of front-end routing, including but not limited to some knowledge points:

  • What is front-end routing?
  • What is the difference between hash and history routes?
  • What is the principle of two routes?
  • What are the scenarios used?
  • What are the advantages of each? Single page related??
  • Vue, React what is the router principle derived from the front-end framework?
  • What is the connection between what we call SPA and front-end routing?
  • Why is SPA needed and what benefits does it bring to the project?

.

What is front-end routing

Routing is a way to interact with the back-end server. It is one of the functions of routing to request different resources and pages through different paths

  • The concept of routing comes from the server, 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 unidirectional, that is, THE CHANGE of URL causes UI update (no Page refresh is required).

Why is front-end routing needed

As mentioned above, the concept of routing actually comes from the server side, so why front-end routing? (And very important)

  • Front-end routing started with Ajax, the technical solution that browsers use to implement asynchronous loading. Previously, most websites returned HTML documents directly, requiring users to refresh the page every time they updated, which greatly affected the user experience.
  • To improve the user experience, Microsoft first introduced the iframe tag, which introduced the concept of asynchronous loading and request elements, and then introduced the basic concept of Ajax (Ajax operations have been around since browsers provided the XMLHTTPRequest interface).
  • With Ajax, user interaction doesn’t have to refresh the page every time, and the experience is vastly improved.
  • A more advanced version of the asynchronous interaction experience is SPA, a single page application

Single-page application is not only refresh-free in page interaction, even page jump is refresh-free, in order to achieve single-page application, so there is front-end routing.

Front-end routing implementation essence: In essence, it is to detect url changes, intercept URL addresses, and then parse to match routing rules.

Therefore, to realize front-end routing, two cores need to be solved:

  • How can I change the URL without causing a page refresh?
  • How do I detect URL changes?

You might ask: does the URL refresh the page every time it changes? How does JavaScript detect and intercept urls when the page is refreshed?

Let’s take a look at the features of the two routes:

3. Types and characteristics of front-end routing

1.1 classification – Hash

Principle:

  • 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

When done by hash routing, the url hash is similar to https://jing.com/a/1190000011956628#home

  1. This kind of#. The subsequent hash change does not cause the browser to make a request to the server,
  2. The browser doesn’t request it, so it doesn’t refresh the page.
  3. It will also trigger each time the hash value changeshashchangeThis event,
  4. From this event we can see what changes have been made to the hash value.

If we want to implement a route using hash mode (https://jing.io/jingda-router -> https://jing.io/jingda-router/#/home), we should go through the following process:

The HASH property of the URL interface returns a USVString containing the ‘#’ in the URL identifier and the fragment identifier (commonly known as the URL hash).

var url = new URL(
  "https://developer.mozilla.org/en-US/docs/Web/API/URL/href#Examples"
);
url.hash; // Returns '#Examples'
Copy the code

1.2 classification – history

Principle:

  • History providespushState andreplaceState Two methods that change the PATH part of the URL without causing a page refresh
  • History providesPopstate event similar to 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 done, just not as convenient as hashchange.

Here are some ways to implement these two front-end routes, but before we do, let’s take a look at some of their characteristics

Hash:

  1. The hash value in the URL is just a state of the client, meaning that the hash part is not sent when a request is made to the server
  2. Changes to the hash value add a record to the browser’s access history, so you can switch the hash using the browser’s back and forward buttons
  3. Use the href = ‘#/blue’ attribute to change the hash value of the url when clicking on the tag. Add ‘#/ bule ‘to the tag and trigger hashChange
  4. Hash = ‘#/blue’, the URL will change, and the hashChange event will be triggered
  5. So we can jump (render) the page using the hashChange event to listen for value changes

history:

  1. HTML5 provides the HistoryAPI to implement URL changes, among which the main API is

    • History.pushstate () adds a history record;
    • History.replacestate () replaces the current history directly;
  2. The history.pushState/replaceState methods take three arguments, in order:

    • State: a state object associated with the specified url that is passed in a callback function when the POPState event is triggered. If this object is not needed, null can be used here
    • Title: The title of the new page, but all browsers currently ignore this value, so null can be filled here.
    • Url: The new url must be in the same domain as the current page. The browser’s address bar will display the url.
window.history.pushState(null.null, path);
window.history.replaceState(null.null, path);
Copy the code

3. The implementation of the history routing mode is based on the following features

  • PushState/replaceState API to implement URL changes;
  • We can use the PopState event to listen for URL changes to jump (render) the page;
  • PushState/replaceState or<a>The popState event is not triggered by the tag, but only when the user clicks the browser’s back and forward buttons, or calls the back, Forward, or Go methods using JavaScript. Fortunately, we can intercept calls to pushState/replaceState and a tag clicks to detect URL changes, so we need to manually trigger the page jump (render);

Some problems with history mode

  1. History routing mode is pretty, but it needs background configuration to be fun.

  2. Because our application is a single-page client application, if the background is not properly configured, when the user accesses some unconfigured path directly in the browser, it will return 404, but because there is no #, the browser will still send a request to the server when the user refreshes the page or something like that. To avoid this, the implementation requires server support to redirect all routes to the root page;

  3. If the URL doesn’t match any static resources, it should return the same index.html page that your app relies on.

4. Implementation of front-end Routing (Principle)

4.1. Javascript native methods for routing?

rendering2.1 the hash

The HTML file

<! -- * @file: description * @author: longjing03 * @Date: 2021-11-15 17:45:27 * @LastEditors: longjing03 * @LastEditTime: The 2021-11-22 14:13:50 -- -- >
<! 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>The front-end routing</title>
  </head>
  <body>
    <h1>hello , Hash router!!!</h1>
    <div style="margin-bottom:100px">
      <ul>
        <li><a href="# /">turn white</a></li>
        <li><a href="#/blue">turn blue</a></li>
        <li><a href="#/green">turn green</a></li>
      </ul>
      <button id="btn">The fallback</button>
      <div id="app" style="margin-top:50px; height:100px"></div>
    </div>

    <script>
      function render() {
        app.innerHTML = "Render container" + window.location.hash;
        app.style.backgroundColor = "pink";
      }
      window.addEventListener("hashchange", render);
    </script>
    <script src="./js/router.js"></script>
  </body>
</html>
Copy the code

Js file

/* * @file: description * @author: longjing03 * @Date: 2021-11-15 17:45:32 * @LastEditors: longjing03 * @LastEditTime: The 2021-11-22 14:33:12 * /
class Router {
  constructor() {
    /** * Stores routes as key-value pairs */
    this.routers = new Object(a);/** * The current route URL */
    this.currentUrl = "";
    /** * Record hash */
    this.history = [];
    /** * as a pointer, it defaults to the end of this.history, and points to different hash */ in history depending on the backward direction
    this.currentIndex = this.history.length - 1;
    /** * The default is not back */
    this.isBack = false;
  }
  /** * is defined on the prototype, the last one overwrites the first one, this one does not execute */
  route(path, callback) {
    console.log(1); }}/** * Stores the hash of the route and the corresponding callback function *@param {*} path
 * @param {*} callback* /
Router.prototype.route = function (routes) {
  for (let route of routes) {
    this.routers[route.path] = route.callback || function () {}; }};/** ** when the page is refreshed */
Router.prototype.refresh = function () {
  /** * gets the hash path */ in the current page
  this.currentUrl = window.location.hash.slice("1") | |"/";
  /** ** does not execute */ backward
  if (!this.isBack) {
    if (this.currentIndex < this.history.length - 1)
      this.history = this.history.slice(0.this.currentIndex + 1);
    /** * pushes the current hash route into the array store, moving the pointer forward */
    this.history.push(this.currentUrl);
    this.currentIndex++;
  }
  this.isBack = false;
  /** * executes the current hash path callback function */
  this.routers[this.currentUrl]();
  console.log("refresh");
  console.log(this.history);
  console.log(this.currentIndex);
};

/** * When the page is backward, the rollback process triggers a hashchange that puts the hash back in and increases the index */
Router.prototype.back = function () {
  console.log("back");
  console.log(this.history);
  console.log(this.currentIndex);
  // Set the back operation to true
  this.isBack = true;
  /** * If the pointer is less than 0, there is no hash route, so lock pointer 0 */
  this.currentIndex <= 0
    ? (this.currentIndex = 0)
    : (this.currentIndex = this.currentIndex - 1);
  /** * As it goes back,location.hash should also change * and execute the pointer that currently points to the callback corresponding to the hash route */
  location.hash = ` #The ${this.history[this.currentIndex]}`;
  this.routers[this.history[this.currentIndex]]();
};

/** * init, listen for page loading and hash changes only */
Router.prototype.init = function () {
  /** * change this to point to, otherwise point to window */
  window.addEventListener("load".this.refresh.bind(this), false);
  window.addEventListener("hashchange".this.refresh.bind(this), false);
};

const route = new Router();
/** * initializes */
route.init();
const routes = [
  {
    path: "/".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "#fff"; }, {},path: "/blue".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "blue"; }, {},path: "/green".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "green"; }},];/** * Bind the hash value to cb */
route.route(routes);
window.onload = function () {
  let btn = document.getElementById("btn");
  btn.addEventListener("click", route.back.bind(route), false);
};
Copy the code

2.2 the history

The HTML file

<! -- * @file: description * @author: longjing03 * @Date: 2021-11-22 14:36:51 * @LastEditors: longjing03 * @LastEditTime: The 2021-11-22 14:38:45 -- -- >
<! 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>The front-end routing</title>
  </head>
  <body>
    <h1>hello , History router!!!</h1>
    <ul>
      <li><a href="/">turn white</a></li>
      <li><a href="http://localhost:3001/color/blue">turn blue</a></li>
      <li><a href="http://localhost:3001/color/green">turn green</a></li>
      <li><a href="http://localhost:3001/color/red">turn red</a></li>
    </ul>

    <script src="./js/history.js"></script>
  </body>
</html>
Copy the code

Js file

/* * @file: description * @author: longjing03 * @Date: 2021-11-22 14:36:57 * @LastEditors: longjing03 * @LastEditTime: The 2021-11-22 14:38:58 * /
/** * history Route */
class Router {
  constructor() {
    /** * Stores routes as key-value pairs */
    this.routers = new Object();
  }
}

/** * listen for the popState event */
Router.prototype.bindPopState = function (e) {
  const path = e.state && e.state.path;
  this.routers[path] && this.routers[path]();
};

/** * Stores the path of the route and the corresponding callback function *@param {*} path
 * @param {*} callback* /
Router.prototype.route = function (routes) {
  for (let route of routes) {
    this.routers[route.path] = route.callback || function () {}; }};/** * initializes to directly replace the current history and store it with a state object */
Router.prototype.init = function (path) {
  window.history.replaceState({ path: path }, null, path);
  this.routers[path] && this.routers[path]();
  /** * join event listener */
  window.addEventListener("popstate".this.bindPopState.bind(this), false);
};

/** * update the page to add a history */
Router.prototype.go = function (path) {
  window.history.pushState({ path: path }, null, path);
  this.routers[path] && this.routers[path]();
};

const route = new Router();
route.init(window.location.href);
const routes = [
  {
    path: "http://localhost:3001/".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "#fff"; }, {},path: "http://localhost:3001/color/blue".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "blue"; }, {},path: "http://localhost:3001/color/green".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "green"; }, {},path: "http://localhost:3001/color/red".callback: function () {
      let el = document.body;
      el.style.backgroundColor = "red"; }},];/** * Bind the hash value to cb */
route.route(routes);

/** ** the a tag will redirect the page, preventing */
window.addEventListener(
  "click".function (e) {
    var e = e || window.event;
    var target = e.target || e.srcElement;
    if ((target.tagName = "A")) {
      e.preventDefault();
      route.go(e.target.getAttribute("href")); }},false
);
Copy the code

Ing 2021.11.22 – updated

4.2 The vue-Router principle?

4.3 React-Router principle?

5. Front end routing to SPA?

Some interviews:

  1. Difficult points of the project (some are not clearly stated)
  2. pureComponent
  3. The pros and cons of React-Saga
  4. Introduction to typescript generics
  5. === == Principles of internal type conversion
  6. The React component passes values
  7. Less sass CSS Module uses advantages and so on