React sample code to implement automatic browser refresh

React sample code to implement automatic browser refresh

With single-page applications so popular today, the once amazing front-end routing has become the basic standard of major frameworks. Each framework provides powerful routing functions, which makes routing implementation complicated. It is still a bit difficult to understand the internal implementation of routing, but it is relatively simple if you just want to understand the basic principles of routing implementation. This article provides six versions of native JS/React/Vue for reference, targeting the mainstream implementation methods of front-end routing, hash and history. The implementation code of each version is about 25 to 40 lines (including blank lines).

What is front-end routing?

The concept of routing comes from the server, where routing describes the mapping relationship between URLs and processing functions.

In the Web front-end single-page application SPA (Single Page Application), routing describes the mapping relationship between URL and UI. This mapping is one-way, that is, URL changes cause UI updates (without refreshing the page).

How to implement front-end routing?

To implement front-end routing, two core issues need to be addressed:

How to change the URL without causing a page refresh? How to detect when a URL has changed?

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

Hash Implementation

  • Hash is the part of the URL that begins with the hash ( # ) and ends with it. It is often used as an anchor to navigate within a page. Changing the hash part of the URL does not cause the page to refresh.
  • Monitor URL changes through hashchange events. There are only a few ways to change URLs: change URLs by moving the browser forward and backward,標簽改變URL、通過wind window.location. These changes will trigger hashchange events.

History Implementation

  • History provides two methods: pushState and replaceState. These two methods change the path part of the URL without causing the page to refresh.
  • history provides a popstate event similar to the hashchange event, but the popstate event is somewhat different: the popstate event is triggered when the URL is changed by moving the browser forward or backward, and the popstate event is not triggered when the URL is changed by pushState/replaceState or tags件。好在我們可以攔截pushState/r replaceState calls and label click events to detect URL changes, so monitoring URL changes can be achieved, but it is not as convenient as hashchange.

Native JS version of front-end routing implementation

Based on the two implementation methods discussed in the previous section, the hash version and history version of routing are implemented respectively. The example uses native HTML/JS implementation and does not rely on any framework.

Hash-based implementation

Operation effect:

cbbb0cd6008139afcd5158c5feadfdb5.png

HTML part:

<body>
  <ul>
ref=""> <!-- Define routes -->
    <li><a href="#/home" rel="external nofollow" >home</a></li>
    <li><a href="#/about" rel="external nofollow" >about</a></li>
 
ref=""> <!-- Render the UI corresponding to the route -->
    <div id="routeView"></div>
  </ul>
</body>

JavaScript part:

// The page will not trigger hashchange after loading. Here, we will actively trigger a hashchange event window.addEventListener('DOMContentLoaded', onLoad)
// Listen for route changes window.addEventListener('hashchange', onHashChange)
 
// Routing view var 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'
      return
    case '#/about':
      routerView.innerHTML = 'About'
      return
    default:
      return
  }
}

History-based implementation

Operation effect:

d1469967dcc98af85ee83cc40b039980.png

HTML part:

<body>
  <ul>
    <li><a href='/home'>home</a></li>
    <li><a href='/about'>about</a></li>
 
    <div id="routeView"></div>
  </ul>
</body>

JavaScript part:

// The page will not trigger hashchange after loading. Here, we will actively trigger a hashchange event window.addEventListener('DOMContentLoaded', onLoad)
// Listen for route changes window.addEventListener('popstate', onPopState)
 
// Routing view var routerView = null
 
function onLoad () {
  routerView = document.querySelector('#routeView')
  onPopState()
 
 href=""> //Intercept the default behavior of the <a> tag click event. When clicked, use pushState to modify the URL and update the manual UI, thereby achieving the effect of updating the URL and UI when clicking a link.
  var 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 '/home':
      routerView.innerHTML = 'Home'
      return
    case '/about':
      routerView.innerHTML = 'About'
      return
    default:
      return
  }
}

React version of front-end routing implementation

Hash-based implementation

Operation effect:

ceb8b03a3af741f98955d1fc1d5ea506.png

The usage is similar to react-router:

  <BrowserRouter>
    <ul>
      <li>
        <Link to="/home">home</Link>
      </li>
      <li>
        <Link to="/about">about</Link>
      </li>
    </ul>
 
    <Route path="/home" render={() => <h2>Home</h2>} />
    <Route path="/about" render={() => <h2>About</h2>} />
  </BrowserRouter>

BrowserRouter Implementation

export default class BrowserRouter extends React.Component {
  state = {
    currentPath: utils.extractHashPath(window.location.href)
  };
 
  onHashChange = e => {
    const currentPath = utils.extractHashPath(e.newURL);
    console.log("onHashChange:", currentPath);
    this.setState({ currentPath });
  };
 
  componentDidMount() {
    window.addEventListener("hashchange", this.onHashChange);
  }
 
  componentWillUnmount() {
    window.removeEventListener("hashchange", this.onHashChange);
  }
 
  render() {
    return (
      <RouteContext.Provider value={{currentPath: this.state.currentPath}}>
        {this.props.children}
      </RouteContext.Provider>
    );
  }
}

Route Implementation

export default ({ path, render }) => (
  <RouteContext.Consumer>
    {({currentPath}) => currentPath === path && render()}
  </RouteContext.Consumer>
);

Link Implementation

export default ({ to, ...props }) => <a {...props} href={"#" + to} />;

History-based implementation

Operation effect:

537e863d46a6ae5d5380e909fd086752.png

The usage is similar to react-router:

  <HistoryRouter>
    <ul>
      <li>
        <Link to="/home">home</Link>
      </li>
      <li>
        <Link to="/about">about</Link>
      </li>
    </ul>
 
    <Route path="/home" render={() => <h2>Home</h2>} />
    <Route path="/about" render={() => <h2>About</h2>} />
  </HistoryRouter>

HistoryRouter Implementation

export default class HistoryRouter extends React.Component {
  state = {
    currentPath: utils.extractUrlPath(window.location.href)
  };
 
  onPopState = e => {
    const currentPath = utils.extractUrlPath(window.location.href);
    console.log("onPopState:", currentPath);
    this.setState({ currentPath });
  };
 
  componentDidMount() {
    window.addEventListener("popstate", this.onPopState);
  }
 
  componentWillUnmount() {
    window.removeEventListener("popstate", this.onPopState);
  }
 
  render() {
    return (
      <RouteContext.Provider value={{currentPath: this.state.currentPath, onPopState: this.onPopState}}>
        {this.props.children}
      </RouteContext.Provider>
    );
  }
}

Route Implementation

export default ({ path, render }) => (
  <RouteContext.Consumer>
    {({currentPath}) => currentPath === path && render()}
  </RouteContext.Consumer>
);

Link Implementation

export default ({ to, ...props }) => (
  <RouteContext.Consumer>
    {({ onPopState }) => (
      <a
        href=""
        {...props}
        onClick={e => {
          e.preventDefault();
          window.history.pushState(null, "", to);
          onPopState();
        }}
      />
    )}
  </RouteContext.Consumer>
);

Vue version front-end routing implementation

Hash-based implementation

Operation effect:

5cb30fe18eb6118acce1f1720efb50c9.png

The usage is similar to vue-router (vue-router injects routes through the plug-in mechanism, but this hides the implementation details. In order to keep the code intuitive, Vue plug-in encapsulation is not used here):

    <div>
      <ul>
        <li><router-link to="/home">home</router-link></li>
        <li><router-link to="/about">about</router-link></li>
      </ul>
      <router-view></router-view>
    </div>
 
const routes = {
  '/home': {
    template: '<h2>Home</h2>'
  },
  '/about': {
    template: '<h2>About</h2>'
  }
}
 
const app = new Vue({
  el: '.vue.hash',
  components:
    'router-view': RouterView,
    'router-link': RouterLink
  },
  beforeCreate () {
    this.$routes = routes
  }
})

router-view implementation:

<template>
  <component :is="routeView" />
</template>
 
<script>
import utils from '~/utils.js'
export default {
  data () {
    return {
      routeView: null
    }
  },
  created () {
    this.boundHashChange = this.onHashChange.bind(this)
  },
  beforeMount () {
    window.addEventListener('hashchange', this.boundHashChange)
  },
  mounted () {
    this.onHashChange()
  },
  beforeDestroy() {
    window.removeEventListener('hashchange', this.boundHashChange)
  },
  methods: {
    onHashChange () {
      const path = utils.extractHashPath(window.location.href)
      this.routeView = this.$root.$routes[path] || null
      console.log('vue:hashchange:', path)
    }
  }
}
</script>

router-link implementation:

<template>
  <a @click.prevent="onClick" href=''><slot></slot></a>
</template>
 
<script>
export default {
  props: {
    to: String
  },
  methods: {
    onClick () {
      window.location.hash = '#' + this.to
    }
  }
}
</script>

History-based implementation

Operation effect:

f4708d3c19db588f48ae4dbc686b2d2e.png

The usage is similar to vue-router:

    <div>
      <ul>
        <li><router-link to="/home">home</router-link></li>
        <li><router-link to="/about">about</router-link></li>
      </ul>
      <router-view></router-view>
    </div>
 
const routes = {
  '/home': {
    template: '<h2>Home</h2>'
  },
  '/about': {
    template: '<h2>About</h2>'
  }
}
 
const app = new Vue({
  el: '.vue.history',
  components:
    'router-view': RouterView,
    'router-link': RouterLink
  },
  created () {
    this.$routes = routes
    this.boundPopState = this.onPopState.bind(this)
  },
  beforeMount () {
    window.addEventListener('popstate', this.boundPopState) 
  },
  beforeDestroy () {
    window.removeEventListener('popstate', this.boundPopState) 
  },
  methods: {
    onPopState (...args) {
      this.$emit('popstate', ...args)
    }
  }
})

router-view implementation:

<template>
  <component :is="routeView" />
</template>
 
<script>
import utils from '~/utils.js'
export default {
  data () {
    return {
      routeView: null
    }
  },
  created () {
    this.boundPopState = this.onPopState.bind(this)
  },
  beforeMount () {
    this.$root.$on('popstate', this.boundPopState)
  },
  beforeDestroy() {
    this.$root.$off('popstate', this.boundPopState)
  },
  methods: {
    onPopState (e) {
      const path = utils.extractUrlPath(window.location.href)
      this.routeView = this.$root.$routes[path] || null
      console.log('[Vue] popstate:', path)
    }
  }
}
</script>

router-link implementation:

<template>
  <a @click.prevent="onClick" href=''><slot></slot></a>
</template>
 
<script>
export default {
  props: {
    to: String
  },
  methods: {
    onClick () {
      history.pushState(null, '', this.to)
      this.$root.$emit('popstate')
    }
  }
}
</script>

summary

The core implementation principle of front-end routing is very simple, but when combined with a specific framework, the framework adds many features, such as dynamic routing, routing parameters, routing animation, etc., which makes the routing implementation complicated. This article only analyzes the implementation of the core part of the front-end routing, and provides three implementations of native JS/React/Vue based on hash and history modes, a total of six implementation versions for reference, I hope it will be helpful to you.

All the examples are available in the Github repository: https://github.com/whinc/web-router-principle

refer to

Detailed explanation of several implementation principles of single page routing

Implementation principle of single page application routing: Taking React-Router as an example

This is the end of this article about the sample code for implementing automatic browser refresh in react. For more relevant react browser automatic refresh content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Determine whether the browser is closed or refreshed based on JavaScript (super accurate)
  • JavaScript does not refresh the browser to achieve the forward and backward function
  • Solution to WeChat browser Javascript cannot use window.location.reload() to refresh the page
  • Js intelligent code to determine whether the browser is closed or refreshed
  • In-depth understanding of the browser's various refresh rules
  • Detailed explanation of vuex persistence plug-in to solve the problem of browser refresh data disappearance

<<:  VMware12.0 installation Ubuntu14.04 LTS tutorial

>>:  Detailed explanation of MySQL instance with SSD storage enabled

Recommend

MySQL automatically inserts millions of simulated data operation code

I use Navicat as my database tool. Others are sim...

Detailed explanation of encoding issues during MySQL command line operations

1. Check the MySQL database encoding mysql -u use...

Navicat for MySQL 15 Registration and Activation Detailed Tutorial

1. Download Navicat for MySQL 15 https://www.navi...

VSCode configuration Git method steps

Git is integrated in vscode, and many operations ...

Examples of some usage tips for META tags in HTML

HTML meta tag HTML meta tags can be used to provi...

Usage and principles of provide and inject in Vue3

Preface: When passing data between parent and chi...

Interpretation of the module for load balancing using nginx

Table of contents Two modules for using nginx for...

MySQL slow_log table cannot be modified to innodb engine detailed explanation

background Getting the slow query log from mysql....

Execute initialization sql when docker mysql starts

1. Pull the Mysql image docker pull mysql:5.7 2. ...

Detailed steps for building Portainer visual interface with Docker

In order to solve the problem mentioned last time...

Three ways to refresh iframe

Copy code The code is as follows: <iframe src=...

How to use nginx to simulate blue-green deployment

This article introduces blue-green deployment and...

In-depth understanding of the role of Vuex

Table of contents Overview How to share data betw...