What is the SPA

SPA is short for Single Page Application, which is different from traditional server-side rendering. To put it simply, SPA is a Web project with only one HTML page. Once a page is loaded, Instead of reloading or jumping to a page based on user action, SPA uses JS to dynamically transform HTML content to simulate jumping between multiple views. Vue, React, Angular are all the same today, excluding frame SSR server rendering.

The SPA application page URL has changed without reloading, improving the user experience and reducing server stress. But also brought SEO optimization is difficult, the first screen loading event is long and other problems.

Use front-end routing to control page rendering

Because the front-end page needs to be dynamically rendered according to user operations, the front-end route needs to observe the change of user’s page URL so as to respond to the page.

There are two common route implementations: history and hash

Hash routing principle and implementation

Early front-end routing was implemented with hashes. Changing the HASH value of the URL does not refresh the page. The hash is the anchor point in the A tag in HTML

<a href="#head"></a>
<! -- The href attribute in the tag :#head hashes the URL ---->
Copy the code

The browser provides a HashChange event to listen for hash changes and control page rendering based on the hash changes

Basic implementation of hash routing

class HashRouter{
    constructor(){
        this.currentPath = '/';
        this.routes = {}
    }
    init(){
        //DOMContentLoaded makes the event execute directly after the HTML is rendered
        window.addEventListener('DOMContentLoaded'.this.updateView.bind(this))
        window.addEventListener('hashchange'.this.updateView.bind(this))}updateView(){
        this.currentPath = location.hash.substring(1) | |'/'
        this.routes[this.currentPath] && this.routes[this.currentPath]()
    }
    route(path, callback){
        this.routes[path] = callback
    }
}

const router = new HashRouter();
router.init();

router.route('/page1'.function(){
    document.getElementById('content').innerHTML = 'This is page1'
})
router.route('/page2'.function(){
    document.getElementById('content').innerHTML = 'This is page2'
})
router.route('/page3'.function(){
    document.getElementById('content').innerHTML = 'This is page3'
})

Copy the code

History Routing principle and implementation

The History router is a bit more complicated. For an application, url changes can only be caused by one of three things:

PushState (), history.replacestate ()

So the idea behind the History Router isto listen for a tag click on the page associated with the route to prevent default redirection, and then manually call history.pushState() to make the browser remember the route, and then manually update the view accordingly. In order to listen for the user to manually click on the browser’s forward and back buttons, you also need to listen for popState events and dynamically modify the corresponding view.

class HistoryRouter{
    constructor(){
        this.currentPath = '/';
        this.routes = {}
    }
    init(){
        // The DOMContentLoaded event is used after the page is refreshed
        window.addEventListener('DOMContentLoaded'.this.updateView.bind(this.'/'))
        var that = this
        window.addEventListener('click'.function (e) {
            if(e.target.tagName.toLocaleLowerCase() === 'a' && e.target.getAttribute('data-href')) {
                e.preventDefault()
                var path = e.target.getAttribute('data-href');
                history.pushState({ path: path }, ' ', path)
                that.updateView(path)
            }
        })
        window.addEventListener('popstate'.function (e) {
            if(e.state){
                var path = e.state.path
                that.updateView(path)
            }else{
                that.updateView('/')}}}updateView(path){
        this.currentPath = path
        this.routes[this.currentPath] && this.routes[this.currentPath]()
    }
    route(path, callback){
        this.routes[path] = callback
   }
}

var router = new HistoryRouter();
router.init();
router.route('/page1'.function(){
    document.getElementById('content').innerHTML = 'This is page1'
})
router.route('/page2'.function(){
    document.getElementById('content').innerHTML = 'This is page2'
})
router.route('/page3'.function(){
    document.getElementById('content').innerHTML = 'This is page3'
})
Copy the code

The key difference between the hash and History modes is the implementation of init

In history mode, the URL of the front end must be the same as the URL of the actual request from the back end, and each front end route has a one-to-one correspondence with the back end resource. If there is no corresponding response or resource in the server, it will be 404

For example, use the Nginx configuration

location / {
  try_files $uri $uri/ /index.html;
}
Copy the code

conclusion

This article introduces the principle and implementation of SPA, and the implementation and differences of React Router and Vue Router will be discussed respectively according to React and Vue

❤️ Thank you all

If you found this helpful:

Like support, let more people can also see this content.

Pay attention to the public number xianyu love front end, we learn together and progress together.

If you feel good, you can also read other articles (thanks to friends for their encouragement and support 🌹🌹🌹) :

Nodejs implements periodic crawlers

React-query makes your state management more elegant

Front-end page layout learning artifact

A deep copy of the interview essentials