vue-router ——前端路由实现原理

一、什么是前端路由?

  前端路由是直接找到与地址匹配的一个组件或对象并将其渲染出来。也就是说,通过改变浏览器地址URL,在不重新请求页面的情况下,更新页面视图

二、单页应用与多页应用

  • 单页应用

    • 即 第一次进入页面的时候会请求一个html文件,刷新清除一下。切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容也变化了。

    • 原理:JS会感知到url的变化,通过这一点,可以用js动态的将当前页面的内容清除掉,然后将下一个页面的内容挂载到当前页面上,这个时候的路由不是后端来做了,而是前端来做,判断页面到底是显示哪个组件,清除不需要的,显示需要的组件。这种过程就是单页应用,每次跳转的时候不需要再请求html文件了

  • 多页应用

    • 每一次页面跳转的时候,后台服务器都会给返回一个新的html文档,这种类型的网站也就是多页网站,也叫做多页应用。

    • 原理:用一些超链接来实现页面切换和跳转的

  • 参考:https://www.jianshu.com/p/4c9c29967dd6

三、实现方式

·  vue-router 提供了三种模式来实现前端路由:1.hash模式 2.history模式 3.abstract模式。hash模式与history模式,这两种模式都是通过浏览器接口实现的,除此之外vue-router还为非浏览器环境准备了一个abstract模式,其原理为用一个数组stack模拟出浏览器历史记录栈的功能。这里主要讲解hash模式和history模式实现前端路由的原理。

1.hash模式:监听浏览器地址hash值变化,执行相应的js改变网页内容

  • 本身以及它后面的字符称作hash,可通过window.location.hash属性读取

  • url中可以带有一个hash,比如 http://localhost:9000/#/song 中的hash值就是 #/song

  • hashchange事件触发:

    • 直接更改浏览器地址,在最后面增加或者改变#hash

    • 通过改变locatin.href或者location.hash的值(window.location.href:表示重定向,后面跟着的是完整的url地址。window.location.hash:得到的是锚链接)

    • 通过触发点击带锚点的链接

    • 浏览器前进后退可能导致hash的变化,前提是两个网页地址中的hash值不同

  • hash实现前端路由的原理:
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>Hash实现前端路由的原理</title>
        </head>
        <body>
            <!-- 跳转组件是由a标签实现  -->
            <a href="/1" class="spa">1</a>
            <a href="/2" class="spa">2</a>
            <a href="/3" class="spa">3</a>
            <script>
                document.querySelectorAll('.spa').forEach(item => {
                    item.addEventListener('click', e => {
                        // 阻止a标签的默认事件
                        e.preventDefault();
                        //帮助改变url地址
                        let link = item.textContent  //获取文本内容
                        location.hash = link;
                        
                    },false)
                })
                //监听路由的变化
                window.addEventListener('hashchange', e => {
                    console.log({
                        location:location.href,
                        hash:location.hash
                    })
                })
            </script>
        </body>
    </html>

2.history模式:利用history API实现url地址改变,网页内容改变

  • History 对象是 window 对象的一部分,可通过 window.history 属性对其进行访问。HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

  • History对象方法:
    • history.back()  #加载 history 列表中的前一个 URL
    • history.forward()  #加载 history 列表中的下一个 URL
    • history.go(n)  #加载 history 列表中的某个具体页面   
  • HTML5新接口:history.pushState(stateObj,title,url)

    • window.history.pushState(stateObj, title, targetURL);
      @状态对象:传给目标路由的信息,可为空
      @页面标题:目前所有浏览器都不支持,填空字符串即可
      @可选url:目标url,不会检查url是否存在,且不能跨域。如不传该项,即给当前url添加sateObj
    • pushState方法不会触发页面刷新,只是导致history对象发生变化,改变网址,网页不会真的跳转,本质上网页还停留在原页面

    • 这里的url受到同源策略的限制

  • popstate事件触发:每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件
    • popstate事件会在点击后退、前进按钮(或调用history.back()history.forward()history.go()方法)时触发。history.pushState()或者history.replaceState()形成的历史节点中前进后退会形成页面切换。注意:仅仅调用pushState方法或replaceState方法,并不会触发该事件,只有用户点击浏览器倒退按钮 和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发
    • 用法:使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)
  • history实现前端路由的原理:
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title>History实现前端路由的原理</title>
        </head>
        <body>
            <!-- 跳转组件是由a标签实现  -->
            <a href="/1" class="spa">1</a>
            <a href="/2" class="spa">2</a>
            <a href="/3" class="spa">3</a>
            <script>
                //监听a标签
                //querySelectorAll找出所有匹配的节点并返回数组.
                document.querySelectorAll(".spa").forEach(item => {
                    item.addEventListener("click",e=>{
                        // 阻止a标签的默认事件
                        e.preventDefault();
                        //帮助改变url地址
                        let link = item.textContent  //获取文本内容
                        
                        // 检查是否支持history和pushState
                        if(window.history && window.history.pushState){
                            //支持History API
                            window.history.pushState({name:"history"},link,link)
                        }else{
                            //不支持,可以使用一些Ployfill插件库来实现(Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能。)
                        }
                    },false)
                })  
                
                //监听路由
                window.addEventListener('popstate',e=>{
                    console.log({
                        location:location.href,
                        state:e.state
                    })
                })
                
            </script>
        </body>
    </html>

3.history和hash两种模式比较

  • pushState设置的新URL可以是与当前同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL

  • pushState设置的新URL可以与当前URL一模一样,这样也会把记录添加到栈中;而hash设置的新值必须与原来不一样才会触发记录添加到栈中

  • pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串

  • pushState可额外设置title属性供后续使用

4.history模式的一个问题

  • 对于单页应用来讲,理想的使用场景是仅在进入应用时加载index.html,后续的网络操作通过Ajax完成,不会根据URL重新请求页面,但是难免遇到特殊情况,比如用户直接在地址栏输入并回车,浏览器重启重新加载应用等。

  • 例子:

    • 在hash模式下,对于http://oursite.com/#/user/id 如果重新请求,只会发送http://oursite.com 故在hash模式下遇到根据URL请求页面的情况不会有问题。hash模式仅改变hash部分的内容,而hash部分是不会包含在HTTP请求中的

    • 在history模式下,对于http://oursite.com/#/user/id 会将该URL修改得和正常请求后端的URL一样,在此情况下重新向后端发送请求,如后端没有配置对应/user/id的路由处理,则会返回404错误

  • 解决办法:官方推荐的解决办法是在服务器端增加一个覆盖所有情况的候选资源:如果URL匹配不到任何静态资源,则应该返回一个index.html页面,这个页面就是你app依赖的页面。同时这么做以后,服务器就不会再返回404错误页面,因为对于所有路径都会返回index.html文件。为了避免这种情况,在Vue应用里面覆盖所有的路由情况,然后在给出一个404页面。

posted @ 2020-03-04 11:12  蛋片鸡  阅读(1014)  评论(0编辑  收藏  举报