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
History Implementation
Native JS version of front-end routing implementationBased 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 implementationOperation effect: 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 implementationOperation effect: 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 implementationHash-based implementationOperation effect: 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 implementationOperation effect: 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 implementationHash-based implementationOperation effect: 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 implementationOperation effect: 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> summaryThe 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 toDetailed 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:
|
<<: VMware12.0 installation Ubuntu14.04 LTS tutorial
>>: Detailed explanation of MySQL instance with SSD storage enabled
Table of contents Overview Build Process Related ...
There are some tags in XHTML that have similar fu...
background: In MySQL, if there is a limited level...
1. Linux under VMware Workstation: 1. Update sour...
Preface This article was written by a big shot fr...
Table of contents Preface Generation of redo log ...
Openlayers is a modular, high-performance and fea...
Table of contents Passing parameters between pare...
1. Create a test table CREATE TABLE `mysql_genara...
The blogger hasn't used MySQL for a month or ...
1. Replication Principle The master server writes...
mysql-8.0.19-winx64 downloaded from the official ...
trigger: Trigger usage scenarios and correspondin...
Table of contents 01 Common controllers in k8s RC...
Use native JavaScript to simply implement the cou...