啊?Vue是有三种路由模式的?尊嘟假嘟?

前言: 文章没有乱其八糟的图片,可在任意时刻食用,宝 ~~~, 懂你噢~~😘


众所周知,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 值发生了哪些变化。

js 复制代码
window.addEventListener('hashchange', ()=>{
    // 通过 location.hash 获取到最新的 hash 值
    console.log(location.hash);
});

使用:

html 复制代码
<!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三个方法,对应浏览器的前进,后退,跳转操作。就是浏览器左上角的前进、后退等按钮进行的操作。

js 复制代码
history.go(-2);//后退两次
history.go(2);//前进两次
history.back(); //后退
hsitory.forward(); //前进

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

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

使用:

html 复制代码
<!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

方法一:

nginx 复制代码
    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)

nginx 复制代码
  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 页面支持。

关于history和hash模式思考:

看了这两种的模式的基本原理,你会发现,hash 模式和 history 模式都属于基于浏览器自身的特性相关的API和特性进行上层搭建,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由 。所以,对于我们看待一个新知识的时候,我们刚开始会觉得它们是一个金字塔,但到最后我们会发现这个金字塔还是这个金字塔,只不过我们把他倒转了。空中楼阁的那颗隐藏的柱子(本质)被我们找到了,而发现了这个"柱子"我们就已经要了解上层建筑的怎么搭建起来的了。


落尾:


大家好,我是 KAIHUA ,一个来自阿卡林省 目前在深圳前端练习生 + ikun

从这周开始,我想试试每周复盘一次,总结出至少一个知识点,目的是尽快给自己的反馈,将自己产品一样快速迭代上升,希望可以坚持✊。

如果有什么相关错误,望大家指正,感谢感谢!!!(第一次写文章,还在学习中,嘿嘿🤭)

下一篇文章应该会是关于 前端基建 方向的,希望早一点归纳出,和大家沟通交流...

各位彦祖/祖贤,fan yin (欢迎)关注点赞收藏,将这泼天的富贵带点给我吧😍

一起加油!!! giao~~~🐵🙈🙉

相关推荐
gnip20 小时前
链式调用和延迟执行
前端·javascript
SoaringHeart21 小时前
Flutter组件封装:页面点击事件拦截
前端·flutter
杨天天.21 小时前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu21 小时前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
Jinuss21 小时前
Vue3源码reactivity响应式篇之watch实现
前端·vue3
YU大宗师21 小时前
React面试题
前端·javascript·react.js
木兮xg21 小时前
react基础篇
前端·react.js·前端框架
ssshooter21 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
IT利刃出鞘1 天前
HTML--最简的二级菜单页面
前端·html
yume_sibai1 天前
HTML HTML基础(4)
前端·html