最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实现前端路由hash、history模式没那么简单

    正文概述 掘金(南橘北枳丶)   2021-02-15   534

    hash模式:


    这个模式比较简单,当我们修改一个html文件的hash时,也就是URL地址后面加一个#value浏览器是不会发送请求的,且即使发送请求(如刷新页面)也不会携带上hash,利用这个特性我们可以通过监听hash的变化从而根据#后面的参数操作DOM完成相应的页面跳转。

    这里着重要掌了解三个地方:

    • 修改hash通过a标签就可以完成,<a href="#1">显示1</a>
    • 监听hash的改变通过hashchange事件监听
    • 通过window.location.hash获取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>
        <style>
            #page1,#page2,#page3,#page4,#page404 {
                display: none;
            }
    
        </style>
        
    </head>
    <body>
        <a href="#1">显示1</a>
        <a href="#2">显示2</a>
        <a href="#3">显示3</a>
        <a href="#4">显示4</a>
        <hr>
        <div id="app">
        </div>
        <div id="page1">1</div>
        <div id="page2">2</div>
        <div id="page3">3</div>
        <div id="page4">4</div>
        <div id="page404">404 Not Found</div>
    
        <script>
            // 获取所有的页面
            let page1 = document.querySelector('#page1');
            let page2 = document.querySelector('#page2');
            let page3 = document.querySelector('#page3');
            let page4 = document.querySelector('#page4');
            // 建立hash表
            const routeTable = {
                '1': page1,
                '2': page2,
                '3': page3,
                '4': page4
            }
    
            function route() {
                // 获取当前hash
                let number = window.location.hash.slice(1);
                number = number || 1;
                // 根据hash拿到当前页面
                let page = routeTable[number.toString()];
                page = page || document.querySelector('#page404');
                page.style.display = 'block';
                // 拿到容器
                let app = document.querySelector('#app');
                if(app.children.length > 0) {
                    app.innerHTML = '';
                }
                // 渲染页面
                app.appendChild(page);
            }
    
    
            // 初始渲染主页面
            route();
            // 监听hash的变化
            window.addEventListener('hashchange',function(){
                route();
            })
        </script>
    </body>
    </html>
    

    实现前端路由hash、history模式没那么简单
    其兼容性是比较好的,但是由于修改hash不会向浏览器发送请求,所以SEO不友好。

    hastory模式:


    在之前我了解到的history模式无非就是通过history.pushState或者history.replaceState修改URL,然后根据MDN文档给出的会触发popstate事件,我们只要对popstate事件进行监听获取location.pathname像上面一样根据对应的参数修改页面的内容就好了。

    之后我在网上搜索了一下,对于修改URL但不发送请求的方法主要是window.history上面的:

    • back():后退到上一个路由;
    • forward():前进到下一个路由,如果有的话;
    • go(number):进入到任意一个路由,正数为前进,负数为后退;
    • pushState(obj, title, url):前进到指定的 URL,不刷新页面;
    • replaceState(obj, title, url):用 url 替换当前的路由,不刷新页面;


    区别是前三个主要是利用浏览器的历史记录,并不能生成新的URL,但后面两个会让浏览器将新的URL存入历史记录。还有就是pushState是IE10以上才可以使用<。br />
    但是还要注意history模式必须与后端相配合,因为虽然history.pushState在修改URL后页面不会重新加载,但如果我们刷新页面还是会用新的URL去发送请求的,如果此时后端的URL还没有更新那么便会返回404

    接下来详细说一下history.pushState这个API,其作用是在浏览器中添加一条新的历史记录,但不刷新页面。且其是在同源情况下进行的,也就是说其只会在当前URL后添加一下新的内容,这里是MDN的文档大家可以详细去看一下,上面也明确的说了history.pushState会触发popstate这个事件。

    忽略前两个参数,我们主要需要关注第三个参数url便好,这是比较基本的用法:

    window.history.pushState(state,title,url)
    
    
    • state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
    • title:标题,基本没用,一般传null
    • url:设定新的历史纪录的url。新的url与当前url的origin必须是一样的,否则会抛出错误。url可以时绝对路径,也可以是相对路径。

    //如当前url是 https://www.baidu.com/a/,
    //执行history.pushState(null, null, './qq/'),
    //则变成 https://www.baidu.com/a/qq/,
    //执行history.pushState(null, null, '/qq/'),
    //则变成 https://www.baidu.com/qq/
    

    通过这个我们可以发现pushState的第三个参数可以是相对的地址

    说到这里我想对于history模式大家应该有一个大致的了解了,以为文章就这么欢快的结束了?但是如果一切如愿也就没有发这篇文章的意义了。

    接着我去尝试着向上面的hash模式一样写一个简单的案例,我知道自己没有后端配合,但是我想只要调用pushState方法可以出发popstate事件便好了,证明这样是可以完成任务的,但是。。。

    				    history.pushState(null,null,'#333');
            window.addEventListener('popstate',function(){
                console.log(location.pathname);
            })
    


    我写了一个小小的案例,按道理如果我通过pushState修改的hash的话,刷新页面后不是会发送请求的但会触发popload事件,从而获取到对应的pathname。但是我蒙了,控制台啥也没有输出???

      实现前端路由hash、history模式没那么简单
    我想这难道就是新年的第一个BUG吗?于是我只能在网上继续进行苦逼的搜索,终于找到了一篇文章和我遇见了同样的问题。

    原来虽然popstate事件也带着一个state但是,其是无法通过pushStatereplaceState触发的,但是go(),back(),forward()却可以触发。

    于是我立刻去进行了实验:

            window.addEventListener('popstate',function(){
                console.log(location.pathname);
            })
            setTimeout(function(){
                history.back();
            },1000)
    


      实现前端路由hash、history模式没那么简单

    发现这回真的成功了,但是如果popstate无法监听pushState对URL的修改那么,我们究竟如何实现对pushState的监听呢?接下来真正的重点来了。。。

    我们需要创建一个自定义事件new Event('pushState'),当我们调用pushState方法时去手动触发我们自定义的事件,然后对自定义事件进行监听就可以取代popstate事件了。

    对自定义事件不了解的同学可以看MDN Event,其实将其理解为我们自己定义的一个事件就可以了,像click之类的,有了事件之后我们就可以将其绑定在一个元素上了,这个例子我先通过addEventListener绑定到window上。

    绑定之后我们便需要触发绑定的事件了,我们绑定到了什么上我们就需要通过什么元素来触发,触发事件的API是dispatchEvent。其和我们操作DOM触发事件不同的地方在于其是同步的。具体也可以看MDN dispatchEvent。

            let oBox = document.querySelector('.box');
            const andyevent = new Event('andyEvent')
            window.addEventListener('andyEvent',function(){
                console.log('andy创建的事件触发了');
            })
            setTimeout(function(){
                window.dispatchEvent(andyevent);
            },1000)
    


    说了这么多这下回到正题,上面这些只是方便大家来理解new Event()dispatchEvent()两个API的作用,之后我们通过重写history.pushState方法,内部创建一个pushState自定义事件,当调用pushState方法时触发我们的自定义事件,在从而在外部监听便好,这是完成的代码。

            // 重写pushState与replaceState方法
            function addStateListener(){
                function listener(type){
                    let origin = history[type];
                    return function(){
                        let newOrigin = origin.apply(this,arguments);
                        let stateEvent = new Event(type);
                      	// 添加arguments的原因是可以在监听到事件触发时拿到传递给事件的参数
                        stateEvent.arguments = arguments;
                        window.dispatchEvent(stateEvent);
                        return newOrigin;
                    }
                }
                history.pushState = listener('pushState');
                history.replaceState = listener('replaceState');
            }
            addStateListener()
    


    实际测试

    				addStateListener()
            // 监听pushState与replaceState
            window.addEventListener('pushState',function(e){
                console.log(location.pathname,e.arguments[2]);
            })
            window.addEventListener('replaceState',function(e){
                console.log(location.pathname,e.arguments[2]);
            })
            // 使用pushState与replaceState修改URL
            history.pushState(null,null,'#333')
            history.replaceState(null,null,'#666')
    

      

      实现前端路由hash、history模式没那么简单

    好了到这里就真的结束了,后面我们根据location.pathname去截取字符串,或者直接向上面一样通过arguments来获取URL被修改部分的参数,来控制页面某些部分的显示与隐藏便好,这里就不再像hash那样具体的展示了。

    最后:

    最后文章首发于我的微信公众号【南橘前端】,这里还有很多前端精彩文章,欢迎大家多多关注,关于文章内容方面的问题我们也可以多多交流?。

    参考:


    小蚊子:深入理解前端中的 hash 和 history 路由
    大黑豹:hash和history实现以及区别


    起源地下载网 » 实现前端路由hash、history模式没那么简单

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元