美文网首页
vue-router 由hash更换为history模式

vue-router 由hash更换为history模式

作者: 搬个菠萝晒太阳 | 来源:发表于2019-10-15 13:13 被阅读0次

    一、概念阐述

    对于 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: [...]
    })
    
    

    注意,如果前端文件不是部署在服务器的根目录下,还要修改另外几个地方。
    假如部署在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

      <IfModule mod_rewrite.c>
          RewriteEngine On
          RewriteBase /
          RewriteRule ^index\.html$ - [L]
          RewriteCond %{REQUEST_FILENAME} !-f
          RewriteCond %{REQUEST_FILENAME} !-d
          RewriteRule . /index.html [L]
      </IfModule>
    
    

    除了 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

    相关文章

      网友评论

          本文标题:vue-router 由hash更换为history模式

          本文链接:https://www.haomeiwen.com/subject/mcksyctx.html