- A+
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
众所周知,vue路由模式常见的有 history 和 hash 模式,但其实还有一种方式-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 页面支持。