瞧一瞧看一看你也会的前端路由中hash模式和history模式

302 阅读4分钟

前言

    我们在学习vue时一定接触过vue-router路由.

image.png

在路由文件index.js中我们可以引入hash模式和history模式,具体效果如下: hash模式:

image.png

我们可以发现当我们切换路由时,url地址栏中会携带#号。hash模式其实就是在浏览器中的url后面接上#xxxx,这样一段内容会被视为hash值,而hash值的变更是不会引起页面刷新的.

history模式:

image.png

对于history模式我们可以发现在切换路由时url的地址栏是不会携带#

路由的作用是我们修改url地址后加载相对应的组件,这也就是地址栏url和组件UI的一种映射关系.

image.png

那么hash模式和history模式是如何实现的呢?路由的核心理念其实就是当url修改之后浏览器会刷新,但是我们不能让浏览器刷新,且我们需要知道浏览器变更.知道url变更之后然后让相对应的内容展示就好了.

hash模式

我们创建一个html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <ul>
        <li><a href="#/home">首页</a></li>
        <li><a href="#/about">关于</a></li>
    </ul>
    <!-- 展示页面 -->
     <div id="routerView">

     </div>

     <script>
        
     </script>
</body>
</html>

hash模式的实现相对来说比较简单的,浏览器天生自带一个事件叫作hashchange监听hash的变化当浏览器url的hash值发生变化就会触发.

window.addEventListener("hashchange",()=>{
   console.log('hashchange');  
 })

那么我们可以在此时通过location.hash获取到当前urlhash值,然后跟我们创建的路由数组routes里面的值匹配就可以了

const routes = [
            {
                path: "/home",
                component:()=>{
                    return '<h2>首页</h2>'
                }
            },
            {
                path: "/about",
                component:()=>{
                    return '<h2>关于</h2>'
                }
            }
        ]
window.addEventListener("hashchange",()=>{
            console.log('hashchange');
            获取本地的hash值
            console.log(location.hash)
            const index = routes.findIndex((item)=>{
                return  '#' + item.path === location.hash
            })
            console.log(index);
            routerView.innerHTML = routes[index].component()
        })

这样写看似没有问题,但是我们需要注意初次加载页面时hash值是不会变化的,所以就无法展示第一次加载页面展示的内容,所以我们需要在页面加载完毕时也需要加载.所以此时我们将这部分代码打造成一个函数使用

image.png

function renderView(locationhash){
          
            const index = routes.findIndex((item)=>{
                return  '#' + item.path === locationhash
            })
            console.log(index);
            routerView.innerHTML = routes[index].component()
        }

并在页面加载完毕时调用.

//页面加载完毕事件
        window.addEventListener('DOMContentLoaded',()=>{
            renderView(location.hash)
        })

完整代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <ul>
        <li><a href="#/home">首页</a></li>
        <li><a href="#/about">关于</a></li>
    </ul>
    <!-- 展示页面 -->
     <div id="routerView">
     </div>
     <script>
        let routerView = document.getElementById("routerView");
        //vue中的路由
        const routes = [
            {
                path: "/home",
                component:()=>{
                    return '<h2>首页</h2>'
                }
            },
            {
                path: "/about",
                component:()=>{
                    return '<h2>关于</h2>'
                }
            }
        ]
        //页面加载完毕事件
        window.addEventListener('DOMContentLoaded',()=>{
            renderView(location.hash)
        })

        window.addEventListener("hashchange",()=>{
            renderView(location.hash)
        })
        function renderView(locationhash){
            const index = routes.findIndex((item)=>{
                return  '#' + item.path === locationhash
            })
            console.log(index);
            routerView.innerHTML = routes[index].component()
        }
     </script>
</body>
</html>

history模式

对于history模式html5提供的history对象有pushStatereplaceState方法.这两个方法可以用于修改url.

function onLoad(){
         
        }

我们打造了一个onLoad函数,在这个函数中我们需要知道是哪一个a标签触发了点击事件并人为的阻止它触发页面跳转,通过history对象中的pushState方法修改.

通过let linkeList = document.querySelectorAll('a[href]')获取到所有的带有href属性的a标签,然后监听每一个a标签的点击事件

linkeList.forEach((el)=>{
                el.addEventListener('click',(e)=>{
                    //阻止默认行为
                    e.preventDefault()
                    history.pushState(null,'',el.getAttribute('href'))
                    renderView(location.pathname)
                })
            })

pushState(state,title,url)方法接受三个参数,第一个可选参数state,这个参数可以存储任何类型的对象,但是这个参数不会传给服务器,仅提供给客户端。第二个参数也是一个可选参数在旧版本浏览器中当用户返回到这个历史记录条目时会显示这个标题,但是对于现代浏览器并没有作用,第三个参数url是必填的url。

现在我们来看一下浏览器中这个前进后退键

image.png

当你在浏览器上面使用pushState,方法来跳页面就会提供一个popstate方法用于操作浏览器的前进后退 完整代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <ul>
            <li><a href="/home">首页</a></li>
            <li><a href="/about">关于</a></li>
        </ul>

        <div id="routerView">

        </div>
        <script>
            let routerView = document.getElementById("routerView");
            //页面加载完毕事件
            window.addEventListener('DOMContentLoaded',()=>{
                onLoad()
            })
            //当你在浏览器上面使用pushState 方法来跳页面 就会提供popstate
            window.addEventListener('popstate',()=>{//浏览器的前进后退
                renderView(location.pathname)
            })
            const routes = [
                {
                    path: "/home",
                    component:()=>{
                        return '<h2>首页</h2>'
                    }
                },
                {
                    path: "/about",
                    component:()=>{
                        return '<h2>关于</h2>'
                    }
                }
            ]
            function renderView(pathName){
            const index = routes.findIndex((item)=>{
                return  item.path === pathName
            })
            routerView.innerHTML = routes[index].component()
        }
                function onLoad(){
                    let linkeList = document.querySelectorAll('a[href]')
                    linkeList.forEach((el)=>{
                        el.addEventListener('click',(e)=>{
                            //阻止默认行为
                            e.preventDefault();
                            //人为控制跳转
                            // console.log(e);
                            //第三个参数表示跳哪里
                            //可以修改url的地址栏不会引起页面刷新
                            history.pushState(null,'',el.getAttribute('href'))
                            renderView(location.pathname)
                        })
                    })
                }
        </script>

    </body>
    </html>

本文到此就结束了,希望对大家有所帮助!!