This is the sixth day of my participation in the First Challenge 2022. For details: First Challenge 2022.

Front-end routing needs to implement two core, 1. Modify URL without causing page refresh, 2. Detection of URL changes depends on which front-end routing technology you choose, hash and history. How does the front-end routing implement the modification operation when you select the technology?

Antecedents feed

This article takes Vue-Router as an example, and makes some analysis of API source code, and only introduces the history mode and the way of modifying URL of front-end route. The reason is that hash was widely used before 2014. History mode is a new form of the HTML5 standard, and the Hash mode in Vue-Router4 is only a degraded version of History mode (which is modified when history mode cannot be modified). For a more detailed description of the differences between the two modes, see another article, Vue-Router’s different history modes

How to change the URL

In apis provided by native browsers, urls can be modified in several ways

  1. Browser Forward and Back
  2. A label
  3. window.location

In Vue-Router4, the Router instance provides the following navigation methods to change urls or routes

  1. push
  2. replace
  3. go
  4. back
  5. forward

Next, the source code corresponding to the above method will be analyzed

Programmatic navigation

The Router instance provides 5 ways to change the URL. In fact, only 3 ways are implemented in the source code: push, replace, and go. For back, the Router instance provides 5 ways to change the URL. Router/SRC /router.js 1141 router definition back, forward is actually implemented in this way, using code reuse to the extreme

const router: Router = {
  ...
  push,
  replace,
  go,
  back: () = > go(-1),
  forward: () = > go(1),... };Copy the code

go

Go /back/forward is similar to the semantics of browser forward and backward. Back () and window.history.forward() correspond to the browser’s two apis, window.history.back() and window.history.forward(), and the go definition in Vue-Router4 actually says so

  1. window.history.back() == myHistory.go(-1)
  2. window.history.forward() == myHistory.go(1)

So the realization of the go guess guess, its implementation in the router/SRC/history/HTML 5. Ts 320 (here mention a mouth vs code code navigation features, I check the implementation of the go, it just gave me a jump, actually have more than one go, No space for me to choose, here I manually sweat soy beans)

function go(delta: number, triggerListeners = true) {
  if(! triggerListeners) historyListeners.pauseListeners(); history.go(delta); }Copy the code

Back () and history.forward() are also used in the browser’s native implementation of history.go. Note the triggerListeners in your implementation, which determine whether your actions trigger front-end route listeners. Note the small details: front-end routes need to detect URL changes. The history mode detects changes through popState events, window.history.go(), window.history.back(), The popState event is not raised when window.history.forward() is called

replace

Replace is actually the call to replaceState(), which you might need to document in the MDN, links, and notice a little detail called replaceState, Like pushState, it actually creates a new history entry in the browser history, such as when you call the following code from the console of the current page (if you’re looking at it on your phone)

history.pushState(null.""."d");
history.replaceState(null.""."a");
Copy the code

Although the URL passed is pretty ridiculous, it doesn’t stop it from running brushless, it doesn’t make requests, and most importantly, it adds two new history records. If you call this one at https://www.baidu.com, you can see there are actually two records in the history. One is https://www.baidu.com/a and one is https://www.baidu.com/b

In addition, there is a small detail. If you haven’t used replaceState directly on pushState() before, no matter how many times you call replaceState, you just change the history of the current page, not add it

Back to the realization of the vue – router4, in the router/SRC/history/HTML 5. Js 240

function replace(to: HistoryLocation, data? : HistoryState) {
  const state: StateEntry = assign(
    {},
    history.state,
    buildState(
      historyState.value.back,
      // keep back and forward entries but override current position
      to,
      historyState.value.forward,
      true
    ),
    data,
    { position: historyState.value.position }
  );

  changeLocation(to, state, true);
  currentLocation.value = to;
}
Copy the code

Replace here does three things

  1. Create a state object, which is vue-Router’s own state object. This object contains window.history’s own state, as well as the previous history and the previous history, and the data to be passed when encoding
  2. Modify browser history involving replaceState()changeLocation().changeLocation()It’s important that push replace is associated with the browser’s own history
  3. Modify the URL value of the Router instance

Focus on analyze changeLocation (), in the router/SRC/history/HTML 5. Js 203

function changeLocation(to: HistoryLocation, state: StateEntry, replace: boolean) :void {...try {
      history[replace ? 'replaceState' : 'pushState'](state, ' ', url)
      historyState.value = state
    } catch (err) {
      ...
      location[replace ? 'replace' : 'assign'](url)
    }
  }
Copy the code

Vue-router push and replace call changeLocation to modify browser history. And when an error occurs, it will degrade the URL to window.location.

push

Push is often used in data structures like stacks. In vue-Router, push is actually pushed into the browser history stack. In vue-Router’s source code implementation, push and replace are highly consistent. That’s why replace first, just look at the source code, okay

  function push(to: HistoryLocation, data? : HistoryState) {...const currentState = assign(
      {},
      historyState.value,
      history.state as Partial<StateEntry> | null,
      {
        forward: to,
        scroll: computeScrollPosition(),
      }
    )

    changeLocation(currentState.current, currentState, true)

    const state: StateEntry = assign(
      {},
      buildState(currentLocation.value, to, null),
      { position: currentState.position + 1 },
      data
    )

    changeLocation(to, state, false)
    currentLocation.value = to
  }
Copy the code

The purpose of the replace implementation is essentially the same, except for the minor detail that it calls a changeLocation twice, another minor detail! The first time is to update the current page’s history state object information. The update information includes the current page scroll information (when we return to the previous route, we can directly flash to the last visited location), and forward(previous history URL), because the page jumps to a new page, The second changeLocation is the historical state object that creates the jump page

conclusion

The vue-router uses a lot of effort to maintain state objects while modifying the route. A state object stores the information of a history entry (small detail, the serialized size of this state object cannot exceed 640kb), and this state object is the core of the front-end routing to transmit information, maintain state, and navigate correctly

It seems that vue-Router part of the source code, in fact, many of their implementation is based on the browser provided by the native API implementation, is not groundless, to put it bluntly, you can also use this API to drum a wheel out, but there is no need, others made libraries, nested that call a dazzling, All kinds of things have been taken into account for you, so cherish life and stay away from building wheels