vue-router|vue-router 由hash更换为history模式

一、概念阐述 对于 Vue 这类渐进式前端开发框架,为了构建 SPA(单页面应用),需要引入前端路由系统,这也就是 Vue-Router 存在的意义。前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。
为了达到这一目的,浏览器当前提供了以下两种支持:

  1. hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。
    比如这个 URL:[http://www.abc.com/#/hello](http://www.abc.com/#/hello),hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,
    但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
  2. history —— 利用了 HTML5 History Interface 中新增的 pushState()replaceState() 方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 backforwardgo 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
因此,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。
二、为什么变迁 一般场景下,hash 和 history 都可以实现路由跳转,默认是hash模式。那么为什么要变迁呢?理由如下:
1、颜值
“#”符号夹杂在 URL 里看起来确实有些不太美丽,所以要把#去掉。
2、跨域问题
在项目开发过程中,路由跳转带#的url请求和普通接口url请求的混用,产生了令人头疼的跨域问题。需要将url中的“#”去掉。
3、其他
根据 Mozilla Develop Network 的介绍,调用 history.pushState() 相比于直接修改 hash,存在以下优势:
  • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL;
  • pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
  • pushState() 通过 stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
  • pushState() 可额外设置 title 属性供后续使用。
当然,history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。
  • hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 [http://www.abc.com](http://www.abc.com/),因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
  • history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 [http://www.abc.com/book/id](http://www.abc.com/book/id)。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
三、如何变迁 1、前端修改
在创建VueRouter对象的地方添加mode:'history'属性。
const router = new VueRouter({ mode: 'history', routes: [...] })

【vue-router|vue-router 由hash更换为history模式】注意,如果前端文件不是部署在服务器的根目录下,还要修改另外几个地方。
假如部署在Project文件夹下,在new VueRouter对象的时候,要添加一个base属性,如下所示:
const router = new VueRouter({ mode: 'history', base: '/Project/', routes: [...] })

另外,在config-->index.js文件中,将assetsPublicPath属性也改为/Project/,如下所示:
build: { // Template for index.html index: path.resolve(__dirname, '../dist/index.html'), // Paths assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/Project/', }

2、后端配置
Apache
RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L]

除了 mod_rewrite,也可以使用 FallbackResource
nginx
location / { try_files $uri $uri/ /index.html; }

原生 Node.js
const http = require('http') const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { fs.readFile('index.htm', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.htm" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) }) }).listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) })

四、变迁后带来的问题及解决办法 变迁为history之后,url中不再带“#”符号,美观了,也能解决跨域的问题了。但是引起了新的问题。
1、vue页面中的href、window.open()等使用相对路径时,指向的链接自动变的不对了。
解决办法:
  • 使用绝对路径,但是使用绝对路径比较麻烦,不推荐。
  • 查看错误链接,和正确的链接对比,对了或者少了什么,然后进行修正。
参考链接: https://router.vuejs.org/zh/guide/essentials/history-mode.html
https://segmentfault.com/q/1010000010340823/a-1020000011352255

    推荐阅读