当前位置:网站首页>Principles and differences between hash and history

Principles and differences between hash and history

2022-06-24 00:05:00 Front end small tips

Current single page application (SPA) More and more become the front-end mainstream , A major feature of single page applications is the use of front-end routing , The front end directly controls the route jump logic , It is no longer controlled by the back-end personnel , This gives the front end more freedom .

At present, there are two main ways to implement front-end routing :hash Patterns and history Pattern , Here are the details .

1. hash Pattern

For example, when making anchor jump with hyperlinks , You will find ,url Followed by "#id",hash The value is url In the from "#" The part from the beginning to the end of No .

hash When the value changes, the browser will not reissue the request , But it triggers window.hashChange event , If we were hashChange Get the current... From the event hash value , And according to hash Value to modify the page content , The purpose of front-end routing is achieved .

<!-- html: The menu href Set to hash form ,id by app Place page content in  -->
<ul id="menu">
    <li>
        <a href="#index"> home page </a>
    </li>
    <li>
        <a href="#news"> information </a>
    </li>
    <li>
        <a href="#user"> Personal center </a>
    </li>
​
</ul>
​
<div id="app"></div>
// stay window.onhashchange In order to get hash value , According to different values , modify app Different contents in , It has the effect of routing 
function hashChange(e){
    // console.log(location.hash)
    // console.log(location.href)
    // console.log(e.newURL)
    // console.log(e.oldURL)
​
    let app = document.getElementById('app')
    switch (location.hash) {
        case '#index':
            app.innerHTML = '<h1> This is the home page </h1>'
            break
        case '#news':
            app.innerHTML = '<h1> This is the news </h1>'
            break
        case '#user':
            app.innerHTML = '<h1> This is a personal focus </h1>'
            break
        default:
            app.innerHTML = '<h1>404</h1>'
    }
}
window.onhashchange = hashChange
hashChange()

The above implementation method is relatively simple , We can package it again :

class Router {
    constructor(){
        this.routers = []  // Store our routing configuration 
    }
    add(route,callback){
        this.routers.push({
            path:route,
            render:callback
        })
    }
    listen(callback){
        window.onhashchange = this.hashChange(callback)
        this.hashChange(callback)()  // There is no trigger when you first enter the page hashchange, You have to call it alone 
    }
    hashChange(callback){
        let self = this
        return function () {
            let hash = location.hash
            console.log(hash)
            for(let i=0;i<self.routers.length;i++){
                let route = self.routers[i]
                if(hash===route.path){
                    callback(route.render())
                    return
                }
            }
        }
    }
}
​
let router = new Router()
router.add('#index',()=>{
    return '<h1> This is the home page </h1>'
}) 
router.add('#news',()=>{
    return  '<h1> This is the news </h1>'
})
router.add('#user',()=>{
    return  '<h1> This is a personal focus </h1>'
})
router.listen((renderHtml)=>{
    let app = document.getElementById('app')
    app.innerHTML = renderHtml
})

Achieve one Router class , adopt add Method to add a routing configuration , The first parameter is the routing path , The second parameter is render function , Return to the page you want to insert html; adopt listen Method , monitor hash change , And return each route html, Insert into app in . So we have a simple hash route .

2. history Pattern

hash The pattern looks ugly , Bring both "#" Number , We can also take history Pattern ,history It is the normal connection form we usually see .history The pattern is based on window.history Object method .

stay HTML4 in , Has supported window.history Object to control page history jump , Common methods include :

  • history.forward(): Take a step forward in history
  • history.back(): Step back in history
  • history.go(n): Jump in history n step ,n=0 To refresh this page ,n=-1 To go back one page .

stay HTML5 in ,window.history Object has been extended , Newly added API Include :

  • history.pushState(data[,title][,url]): Append a record to the history
  • history.replaceState(data[,title][,url]): Replace the current page's information in history .
  • history.state: Is an attribute , You can get the current page state Information .
  • window.onpopstate: It's an event , Click the browser Back button or js call forward()back()go() Trigger when . A listening function can be passed in event object ,event.state That is, through pushState() or replaceState() Method passed in data Parameters

history The pattern principle can be understood in this way , First we need to transform our hyperlinks , Add... To each hyperlink onclick Method , Prevent default hyperlinks from jumping , change to the use of sth. history.pushState or history.replaceState To change... In the browser url, And modify the page content . Because through history Of api adjustment , It does not send a request to the back end , Therefore, the purpose of front-end routing is achieved .

If the user uses the browser's forward and backward buttons , It triggers window.onpopstate event , The listening page modifies the page content according to the routing address .

You don't have to use hyperlinks , Any element can be used as a menu , Just pass in the click event history Adjust it .

<!--html:-->
<ul id="menu">
    <li>
        <a href="/index"> home page </a>
    </li>
    <li>
        <a href="/news"> information </a>
    </li>
    <li>
        <a href="/user"> Personal center </a>
    </li>
​
</ul>
<div id="app"></div>
//js:
// Transform hyperlinks , Prevent default jump , The default jump will refresh the page 
document.querySelector('#menu').addEventListener('click',function (e) {
    if(e.target.nodeName ==='A'){
        e.preventDefault()
        let path = e.target.getAttribute('href')  // Get the hyperlink href, Change it to pushState Jump , Do not refresh page 
        window.history.pushState({},'',path)  // Modify the url Address 
        render(path)  // according to path, Change page content 
    }
})
​
function render(path) {
    let app = document.getElementById('app')
    switch (path) {
        case '/index':
            app.innerHTML = '<h1> This is the home page </h1>'
            break
        case '/news':
            app.innerHTML = '<h1> This is the news </h1>'
            break
        case '/user':
            app.innerHTML = '<h1> This is a personal focus </h1>'
            break
        default:
            app.innerHTML = '<h1>404</h1>'
    }
}
// Monitor browser forward and backward Events , And render the page according to the current path 
window.onpopstate = function (e) {
    render(location.pathname)
}
// Enter the page for the first time to display the home page 
render('/index')

The above writing is too low, We can use classes to encapsulate , adopt add Method to add a route , adopt pushState To jump , Change the jump mode of all hyperlinks during initialization :

class Router {
    constructor(){
        this.routers = []
        this.renderCallback = null
    }
    add(route,callback){
        this.routers.push({
            path:route,
            render:callback
        })
    }
    pushState(path,data={}){
        window.history.pushState(data,'',path)
        this.renderHtml(path)
    }
    listen(callback){
        this.renderCallback = callback
        this.changeA()
        window.onpopstate = ()=>this.renderHtml(this.getCurrentPath())
        this.renderHtml(this.getCurrentPath())
    }
    changeA(){
        document.addEventListener('click', (e)=> {
            if(e.target.nodeName==='A'){
                e.preventDefault()
                let path = e.target.getAttribute('href')
                this.pushState(path)
            }
        })
    }
    getCurrentPath(){
        return location.pathname
    }
    renderHtml(path){
        for(let i=0;i<this.routers.length;i++){
            let route = this.routers[i]
            if(path===route.path){
                this.renderCallback(route.render())
                return
            }
        }
    }
}
​
let router = new Router()
router.add('/index',()=>{
    return '<h1> This is the home page </h1>'
})
router.add('/news',()=>{
    return  '<h1> This is the news </h1>'
})
router.add('/user',()=>{
    return  '<h1> This is a personal focus </h1>'
})
router.listen((renderHtml)=>{
    let app = document.getElementById('app')
    app.innerHTML = renderHtml
})

Of course , The above implementation is only a very rudimentary demo, It cannot be used in real development scenarios , Just to deepen the understanding of front-end routing .

3. hash Patterns and history The difference between patterns

  • hash The pattern is ugly ,history The model is more elegant
  • pushState Set up new URL It can be related to the current URL Any of the same origin URL; and hash You can only modify # Back section , Therefore, you can only set the URL
  • pushState Set up new URL Can be compared with the current URL As like as two peas , This also adds records to the stack ; and hash The new value set must be different from the original value to trigger the record to be added to the stack
  • pushState adopt stateObject You can add any type of data to the record ; and hash Only short strings can be added
  • pushState Additional settings are available title Property for subsequent use
  • hash compatible IE8 above ,history compatible IE10 above
  • history The pattern requires back-end cooperation to point all accesses to index.html, Otherwise, the user refreshes the page , It can lead to 404 error
原网站

版权声明
本文为[Front end small tips]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/11/20211123193239377R.html