This article mainly explains the common single-page application routing library implementation ideas. Through its implementation method we can understand some browsers work, and in-depth learning to understand some of the commonly used apis.

For business purposes, we monitor the number of page visits. For a traditional multi-page application, this is easy, we just need to report after the page is loaded. But for single-page apps, it’s not so simple. The first step is to understand the mechanics of a single page app, how it works, and how we can listen for changes on the page.

SPA & MPA

SPA

SPA(Single Page Applications), Single Page Applications. Switch between different modules/functions in the application on the same page.

Usually the HASH of urls between modules will be different. Such as https://test.com/#/login, https://test.com/#/user

MPA

Multiple Page Applications MAP(Multiple Page Applications) This is when different modules/features of an application are rendered between different pages.

Usually HTML pages are different, such as https://test.com/login.html, https://test.com/user.html

The difference between

Typical single-page apps have a better user experience and faster page switching. Multi-page apps are more SEO friendly and have faster home page loading times.

The implementation principle of SPA

After knowing about the single-page application, we can make it clear that the route switching of the single-page application is carried out under the same page. This means that the browser does not refresh/skip the page. But its URL did change. Since hash changes do not cause browser refresh behavior, SPA relies on them.

1. Change the hash directly

So the first way we can think of implementing SPA on our own is to manually change the hash and then listen for the hash change to change the routing view.

The principle of

window.addEventListener('hashchange', renderView)

window.location.hash = '/login'
Copy the code

DIY

<div>
  <a href="#/login">The login</a>
  <a href="#/about">about</a>
</div>
<div class="view"></div>
Copy the code
const $view = document.querySelector(".view");

const routes = {
  "/login": "LOGIN"."/about": "ABOUT"};window.addEventListener("hashchange".(e) = > {
  const route = getHash(e.newURL);
  if(route) { renderView(route); }});function renderView(route) {
  const content = routes[route];
  $view.innerHTML = content;
}

function getHash(url) {
  const href = window.location.href;
  const i = href.indexOf("#");

  const hash = i >= 0 ? href.slice(i + 1) : "";

  return hash;
}
Copy the code

2. pushState

The principle of

Of course, SPA is already well implemented by modifying hash directly, but we can do even better, and more elegant, with the more powerful method that the browser provides us, which is the HistoryAPI.

Both history.pushState and history.replaceState can be used to change the browser URL without causing a refresh.

DIY

We just need to change part of the code

<div>
  <a href="/login">The login</a>
  <a href="/about">about</a>
</div>
<div class="view"></div>
Copy the code
const $links = document.querySelectorAll("a");

$links.forEach((element) = > {
  element.addEventListener("click".(e) = > {
    e.preventDefault();

    const path = e.target.getAttribute("href");
    renderView(path);
    window.history.pushState({}, ""."#" + path);
  });
});
Copy the code

Pay attention to

Note that pushState and replaceState do not trigger hashchange, popState events. That is, we need to change the view while pushing State/replaceState.

3. The difference between

Either hash change or pushState inserts a record into the history.

But there are many differences:

  • usehistory.pushState()You can changereferrer.
  • usehistory.pushState()You can change any cognate URL instead of limiting it tohashValue.

That is to say, you can call the history at https://test.com/foo.html. PushState (null, “, ‘/ bar. HTML) change the URL to https://test.com/bar.html and do not refresh the page, It also doesn’t request /bar.html (assuming it doesn’t exist). What’s the use? You can see that this URL is already the same as the MPA URL, which is good for SEO. But if the user on the https://test.com/bar.html page refresh, that is bad, because there is no bar. The HTML this resource. So that’s why you need support from the back end if the SPA is going to use this URL presentation pattern. Server needs to put all page agent to the entry page https://test.com/foo.html. Then do route jump by the entrance page.

  • usehistory.pushState(state, title, url)You can pass in a state objectstate.

That is, you can attach some data to the page, whereas in hash mode you need to attach the data to the URL.

Implementation principle of Vue Router

The first thing that can be confirmed is that the most basic support for refreshing route switching is the hash and history implementations mentioned above.

Here’s a quick overview of how it changes the view.

// ...
this._router = this.$options.router
// ...
Vue.util.defineReactive(this.'_route'.this._router.history.current)
Copy the code

This.$options. Router is an instance of VueRouter. But what does this._router.history.current represent? $router. Push /this.$router. Replace /this.$router.

Most importantly, there is a new reactive attribute called _route. When a reactive property is updated, components that depend on the property are updated.

As you might expect, the RouterView relies on the _route attribute. It updates the component rendering based on the _route change (reexecutes the render function). The _route in turn matches the components that should have the current route, which is what the RouterView is rendering.

VueRouter has put Vue’s responsive design to good use.

The same principle applies to the ReactRouterDom.

How to listen

Implementation is known, so how do we listen to this thing? No Statechange event yet! Hashchange does exist, but we need a more sophisticated solution.

We can’t listen for changes. We have to intercept calls. Just like the method you used to learn window.onload in the first place.

window.onload = () = > {
  console.log("1");
};

const _onload = window.onload;
window.onload = (e) = > {
  _onload(e);
  console.log(2);
};
Copy the code

But now we have the more advanced method Proxy, and here is a simple example.

history.pushState = new Proxy(history.pushState, {
  apply: function (target, thisBinding, args) {
    console.log('is this it? ');
    returntarget.apply(thisBinding, args); }});Copy the code

In the same way, we can also delegate console, XHR. To listen for any activity on the page!

The downside is that Proxy is ES6 content and only works with modern browsers. Older browsers require Polyfill.

History of hits

  1. How to complete a Business page in 10 minutes – the art of Vue packaging
  2. Novice can also understand the virtual rolling method
  3. Axios source code analysis

Original text – My little station