记录--啊?Vue是有三种路由模式的?

发布时间 2023-11-11 19:12:26作者: 林恒

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

众所周知,vue路由模式常见的有 historyhash 模式,但其实还有一种方式-abstract模式(了解一哈~)

别急,本文我们将重点逐步了解: 路由 + 几种路由模式 + 使用场景 + 思考 + freestyle


路由概念

路由的本质就是一种对应关系,根据不同的URL请求,返回对应不同的资源。那么url地址和真实的资源之间就有一种对应的关系,就是路由。


路由模式由来

对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-Router 存在的意义。而前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求

为了达到这一目的,就产生了我们的 —— 路由模式


三种路由模式详解


hash模式

示例: www.ikun.com/#/kun,hash 的值为 #/kun。

概述:

地址栏 URL 中有 # 符号,后面就是 hash 值的变化(此 hash 不是密码学里的散列运算)。特点是:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端没有影响,改变后面的 hash 值,它不会向服务器发出请求,因此也就不会 刷新页面/重新加载页面

每次 hash 值发生改变的时候,会触发 hashchange 事件。因此我们可以通过监听该事件,来知道 hash 值发生了哪些变化。

window.addEventListener('hashchange', ()=>{
    // 通过 location.hash 获取到最新的 hash 值
    console.log(location.hash);
});
使用:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>hash路由</title>
</head>
<body>
​
  <ul>
    <!-- 通过标签导航  声明式导航 -->
    <!-- location.href='#/home' js方式进行导航切换 编程式导航 -->
    <li><a href="#/home">首页</a></li>
    <li><a href="#/about">关于</a></li>
  </ul>
​
  <div id="routerView"></div>
​
  <script>
    const routerRender = () => {
      // 每次都置空hash
      let html = ''
      // 根据地址栏hash值的不同返回对应的资源
      try {
        // 如果hash值为空就给一个home
        let hash = location.hash || '#/home'
        html = component[hash.slice(2)]()
      } catch (error) {
        html = `<div>404</div>`
      }
      // 渲染到页面上
      document.getElementById('routerView').innerHTML = html
    }
​
    const component = {
      home() {
        return `<div>home页面</div>`
      },
      about() {
        return '<div>关于页面</div>'
      }
    }
​
    window.onload = function () {
      routerRender()
    }
​
    // 事件,监听地址栏中的hash值变化,实现回退
    window.addEventListener('hashchange', routerRender)
      
  </script>
</body>
</html>

优缺点:

优点:hash模式兼容性、安全性很强,刷新浏览器,页面还会存在

缺点:地址栏不优雅,有#存在,不利于seo,记忆困难

注意:

hash 模式既可以通过声明式导航,也可以通过编程式导航,上面的案例展示的是声明式导航。而下面将要讲到的 history 模式只能通过编程式导航实现,因为 history 是 js 对象。


history模式

示例: www.ikun.com/kun,地址栏中没有#,路由地址跟正常的url一样

概述:

history —— 利用了 HTML5 History API 为浏览器的全局 history 对象增加的 pushState()replaceState() 方法,可以对浏览器历史记录栈进行修改。(新增特性,所以浏览器需考虑IE9以及以下的版本带来的问题)。当地址栏的history状态发生变化时 切换了router-view渲染的组件 来"欺骗"用户 到达切换新网页的效果,需要后端配合

History 还包括back、forward、go三个方法,对应浏览器的前进,后退,跳转操作。就是浏览器左上角的前进、后退等按钮进行的操作。

history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进

只要历史栈有信息发生改变的话,window对象中提供的 popstate 事件就会监听到历史栈的改变,就会触发该事件。

history.pushState({},title,url); // 向历史记录中追加一条记录
history.replaceState({},title,url); // 替换当前页在历史记录中的信息。
​
window.addEventListener('popstate', function(e) {
  console.log(e)
})

使用:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>history模式</title>
</head>
​
<body>
  <ul>
    <li><a href="/home">首页</a></li>
    <li><a href="/about">关于</a></li>
  </ul>
​
  <div id="routerView"></div>
​
  <script>
    const component = {
      home() {
        return `<div>home页面</div>`
      },
      about() {
        return '<div>关于页面</div>'
      }
    }
​
    const routerRender = pathname => {
      let html = ''
      try {
        html = component[pathname]()
      } catch (error) {
        html = `<div>404</div>`
      }
      document.getElementById('routerView').innerHTML = html
    }
​
    // history模式,它的路由导航,只能通过js来完成 , history它是js对象
    // 给链接添加点击事件
    document.querySelectorAll('a').forEach(node => {
      node.addEventListener('click', function (evt) {
        // 阻止a标签的默认跳转行为
        evt.preventDefault()
        // 跳转到指定的地址,能回退
        // history.pushState
        // 跳转到指定持址,不能回退
        // history.replaceState
        history.pushState({}, null, this.href)
        // 渲染
        routerRender(this.href.match(//(\w+)$/)[1])
      })
    })
​
    // 在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻渲染 home 中的标签
    window.onload = () => {
      routerRender('home')
    }
​
    // 回退
    window.addEventListener('popstate', function () {
      routerRender(location.pathname.slice(1))
    })
​
  </script>
</body>
</html>

优缺点:

缺点:history模式,兼容性较差,刷新页面,页面会404,需要服务器端配置支持

优点:地址栏更优雅,方便记忆,有利于有seo


刷新页面出现404原因以及解决:

原因:

因为vue项目中路由hash模式改为了history模式,由于hash模式时url带的#号后面是哈希值不会作为url的一部分发送给服务器,而history模式下当刷新页面之后浏览器会直接去请求服务器,而服务器没有这个路由,于是就出现404

因为我们的应用是单页客户端应用,当使用 history 模式时,URL 就像正常的 url,可以直接访问www.ikun.com/kun/love,但是因为 vue-router 设置的路径不是真实存在的路径,所以刷新就会返回404错误

解决方法(后端配合,这里讲的是nginx配置):

在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。也就是在服务端修改404错误页面的配置路径,让其指向到index.html

方法一:

    location /{
        root   /data/nginx/html;
        index  index.html index.htm;
        if (!-e $request_filename) {
            rewrite ^/(.*) /index.html last;
            break;
        }
    }

方法二: (vue.js官方教程里提到的https://router.vuejs.org/zh-cn/essentials/history-mode.html)

  server {
        listen       8888;#默认端口是80,如果端口没被占用可以不用修改
        server_name  localhost;
        root        E:/vue/my_project/dist;#vue项目的打包后的dist
        location / {
            try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
            index  index.html index.htm;
        }
        #对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件
        #因此需要rewrite到index.html中,然后交给路由在处理请求资源
        location @router {
            rewrite ^.*$ /index.html last;
        }
        #.......其他部分省略
  }

abstract模式

abstract模式----适用于所有JavaScript环境,例如服务器端使用Node.js。如果没有浏览器API,路由器将自动被强制进入此模式。

abstract 是一种与浏览器分离的路由模式,本身是用来在不支持浏览器API的环境中,充当fallback,而不论是hash还是history模式都会对浏览器上的url产生作用。

利用abstract这种与浏览器分离的路由模式,我们可以在已存在的路由页面中内嵌其他的路由页面,而保持在浏览器当中依旧显示当前页面的路由path。


使用场景


history --- 颜值性(强迫症患者推荐,更友好的URL格式、SEO支持)

一般场景下,hash 和 history 都可以,除非你更在意颜值,# 符号夹杂在 URL 里看起来确实有些不太美丽。我们可以用路由的 history 模式,充分利用 history.pushState API 来完成URL 跳转而无须重新加载页面。如果需要更好的SEO支持,并且愿意进行服务器端配置,history 模式是很好的选择

调用 history.pushState() 相比于直接修改 hash,还存在以下优势

1、pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 #后面的部分,因此只能设置与当前 URL 同文档的 URL;

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

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

4、pushState() 可额外设置 title 属性供后续使用。

hash ---- 安全兼容,不需要后端协助

SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候

hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 www.ikun.com ,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误

abstract模式 ---- 特殊场景

abstract模式----适用于所有JavaScript环境(浏览器端和服务端),例如服务器端使用Node.js。

像上文说的,可以利用abstract这种与浏览器分离的路由模式,在已存在的路由页面中内嵌其他的路由页面,而保持在浏览器当中依旧显示当前页面的路由path。


小结


选择使用 hash 模式还是 history 模式,主要取决于你的具体需求和项目要求。如果你的应用不需要考虑SEO,并且不涉及服务器端的重定向和处理,Hash模式是一种简单且易于使用的选择。如果你需要更友好的URL格式、更好的SEO支持,并且愿意进行服务器端配置,那么history 模式是更好的选择。

结合自身例子,对于一般形式的 Web 开发场景,个人比较习惯用用 history 模式,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。

本文转载于:

https://juejin.cn/post/7297035708429484066

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。