Angular, React and Vue all recommend the single-page application SPA development mode, which replaces the least modified part of the DOM Tree during route switching to reduce the huge performance loss caused by page hopping of multi-page applications. They all have their own typical routing solutions, @angular/ Router, React-Router, vue-Router.

In general, these routing plug-ins always provide two different routing modes: Hash and History also sometimes provide non-browser routing. Abstract: In vue-Router, the appearance mode is used to provide a consistent high-level interface for different routing modes, allowing us to switch between different routing modes more decoupled.

It is worth mentioning that besides the appearance difference between Hash and History, there is another difference: Hash state saving needs to be passed separately, whereas HTML5 History native provides the ability to customize state passing, which we can use directly to pass information.

Let’s take a look at the features of these two ways, and provide simple implementation, more complex functions such as lazy loading, dynamic path matching, nested routines, routing aliases, etc., can pay attention to the following vue-Router source code interpretation of the blog.

If you are interested, you can join the wechat group at the end of the article and discuss with us

1. Hash

1.1 the relevant Api

Hash method is a Hash method with a # in the route. The main mechanism is to listen for the change of the URL path identifier after # to trigger the browser hashchange event, and then obtain the current path identifier by obtaining location. Hash, and perform some route jump operations, see MDN

  1. location.href: Returns the full URL
  2. location.hash: returns the anchor part of the URL
  3. location.pathname: Returns the URL pathname
  4. hashchangeEvent: whenlocation.hashThis event is emitted when changes occur

Such as a visit to a path of http://sherlocked93.club/base/#/page1, then the above a few values are:

# http://sherlocked93.club/base/#/page1
{
  "href": "http://sherlocked93.club/base/#/page1",
  "pathname": "/base/",
  "hash": "#/page1"
}
Copy the code

Note: The Hash method uses the equivalent of a page anchor, so it conflicts with the original scrolling method, which leads to the wrong routing path. Therefore, we need to use another method, which we encountered when writing the progress-Catalog plug-in.

1.2 instance

Click hashchange to get the current path and execute the corresponding callback.

class RouterClass {
  constructor() {
    this.routes = {}        // Record the CB of the path identifier
    this.currentUrl = ' '    // Record hash for cb execution
    window.addEventListener('load', () = >this.render())
    window.addEventListener('hashchange', () = >this.render())
  }
  
  /* Initialize */
  static init() {
    window.Router = new RouterClass()
  }
  
  /* Register routes and callbacks */
  route(path, cb) {
    this.routes[path] = cb || function() {}}/* To record the current hash, run cb */
  render() {
    this.currentUrl = location.hash.slice(1) | |'/'
    this.routes[this.currentUrl]()
  }
}
Copy the code

See CodePen for the implementation

If you want to use a script to control the Hash route back, you can record the route that you went through. The route back hop is implemented by assigning a value to location. Hash. This, however, causes the hashchange event to be reraised, entering render a second time. So we need to add a flag bit to indicate whether the render method is entered because of a rollback or a user jump

class RouterClass {
  constructor() {
    this.isBack = false
    this.routes = {}        // Record the CB of the path identifier
    this.currentUrl = ' '    // Record hash for cb execution
    this.historyStack = []  / / hash stack
    window.addEventListener('load', () = >this.render())
    window.addEventListener('hashchange', () = >this.render())
  }
  
  /* Initialize */
  static init() {
    window.Router = new RouterClass()
  }
  
  /* Record that path corresponds to CB */
  route(path, cb) {
    this.routes[path] = cb || function() {}}/* To push the current hash, run cb */
  render() {
    if (this.isBack) {      // If backoff is entered, return is set to false
      this.isBack = false   // The other operations are done in the backoff method
      return
    }
    this.currentUrl = location.hash.slice(1) | |'/'
    this.historyStack.push(this.currentUrl)
    this.routes[this.currentUrl]()
  }
  
  /* Route back */
  back() {
    this.isBack = true
    this.historyStack.pop()                   // Remove the current hash and roll back to the previous hash
    const { length } = this.historyStack
    if(! length)return
    let prev = this.historyStack[length - 1]  // Get the target hash to fall back to
    location.hash = ` #${ prev }`
    this.currentUrl = prev
    this.routes[prev]()                       // Run the corresponding CB command}}Copy the code

Code implementation reference CodePen

2. HTML5 History Api

2.1 the relevant Api

HTML5 provides some routing apis, about the use of this article can refer to the MDN article, here is a list of common apis and their role, the specific parameters of what is not introduced, MDN have

  1. history.go(n): Indicates route redirection. For example, n is2It’s 2 pages ahead, n is2 -If n is 0, the page is refreshed
  2. history.back(): Indicates that the route is backwardhistory.go(-1)
  3. history.forward(): Route forward, equivalent tohistory.go(1)
  4. history.pushState(): Adds a route history record. An error message is displayed if the cross-domain ADDRESS is set
  5. history.replaceState(): Replaces the route history information of the current page
  6. popstateEvent: Triggered when the history of an activity changespopstateEvent, which is also raised when the browser’s forward and back buttons are clicked or when the first three methods are called, seeMDN

2.2 instance

To modify the previous example, use history.pushState to push cb where you want to route the jump, listen for the popState event to retrieve the argument passed to pushState and execute cb, since borrowing the browser’s own Api. So the code looks cleaner

class RouterClass {
  constructor(path) {
    this.routes = {}        // Record the CB of the path identifier
    history.replaceState({ path }, null, path)	// Enter the state
    this.routes[path] && this.routes[path]()
    window.addEventListener('popstate', e => {
      const path = e.state && e.state.path
      this.routes[path] && this.routes[path]()
    })
  }
  
  /* Initialize */
  static init() {
    window.Router = new RouterClass(location.pathname)
  }
  
  /* Register routes and callbacks */
  route(path, cb) {
    this.routes[path] = cb || function() {}}/* Redirects the route and triggers the corresponding route callback */
  go(path) {
    history.pushState({ path }, null, path)
    this.routes[path] && this.routes[path]()
  }
}
Copy the code

Hash mode uses the Hash of a URL to simulate a full URL, so the page does not reload when the URL changes. In History mode, the URL is changed directly. Therefore, some address information is lost during route hops, and static resources cannot be matched when route addresses are refreshed or accessed directly. So you need to configure some information on the server to add a candidate resource to cover all the cases, such as jump index.html, which is generally the page your app depends on. In fact, libraries like Vue – Router are also recommended, and provide common server configurations.

Code implementation reference CodePen


Online posts are mostly different in depth, even some inconsistent, the following article is a summary of the learning process, if you find mistakes, welcome to comment out ~

Reference:

  1. history | MDN
  2. hashchange | MDN
  3. Manipulating the browser history | MDN
  4. Fundamentals of front-end routing – Big History doesn’t talk
  5. History object – JavaScript standard Reference tutorial

PS: Welcome to pay attention to my official account [front-end afternoon tea], come on together

In addition, you can join the “front-end afternoon tea Exchange Group” wechat group, long press to identify the following TWO-DIMENSIONAL code to add my friend, remarks add group, I pull you into the group ~