前端路由如何修改 URL ?

更新日期: 2022-02-07阅读: 1.2k标签: 路由

前端路由需要实现两个核心, 1. 修改 URL 而不引起页面刷新, 2. 检测 URL 的变化, 这两个核心取决于你采用的前端路由技术选择的方案, 即 hash 和 history, 当选择了技术后, 前端路由是怎样去实现修改的操作的呢?


前情提要

本篇文章vue-router 为例, 并对其中的 api 源码进行一定的分析, 并且只会介绍 history 模式, 前端路由修改 URL 的方式, 原因是 hash 是 2014 年以前普遍采用的方式, history 模式是 html5 标准的新方式, 而且在 vue-router4 中 hash 模式只是作为 history 模式的降级处理(当 history 模式无法修改时, 会采用 hash 模式的修改), 关于两种模式更详细的区别和介绍可以参考另一篇文章, vue-router 的不同的历史记录模式详解


改变 URL 的方式

在原生浏览器提供的 API 中,我们可以通过以下几种方式修改 URL

  1. 浏览器前进后退
  2. a 标签
  3. window.location

而在 vue-router4 中, Router 实例提供的修改 URL 或者说路由的导航方式有以下几种

  1. push
  2. replace
  3. go
  4. back
  5. forward

接下来会对上述方法对应的源码进行分析


编程式导航

编程式导航指的是在 vue/JS 中编写导航代码, 前面说了 Router 实例提供了 5 种修改 URL 的方法, 实际在源码中只实现了 3 种, 即 push, replace, go 而对于 back, forward 实际上是 go 的变种, 在 router/src/router.js 1141 的 Router 定义中 back, forward 实际是这样实现的, 把代码复用发挥到了极致了属于是

const router: Router = {
  ...
  push,
  replace,
  go,
  back: () => go(-1),
  forward: () => go(1),
  ...
};


go

go/back/forward 有点接近浏览器前进后退的语义,那 go 的实现究竟和浏览器前进后退有没有关系呢?浏览器的前进后退对应的是浏览器的两个 API, window.history.back() 和 window.history.forward(), 而 vue-router4 中 go 的定义实际上也明说了

  1. window.history.back() == myHistory.go(-1)
  2. window.history.forward() == myHistory.go(1)

所以 go 的实现猜也猜得到了,它的实现在 router/src/history/html5.ts 320 (这里提一嘴 vs code 的代码导航的功能, 我查看 go 的实现的时候,它直接就给我跳了,实际上 go 的实现有多个,没有给我选择的空间,这里我手动流汗黄豆)

function go(delta: number, triggerListeners = true) {
  if (!triggerListeners) historyListeners.pauseListeners();
  history.go(delta);
}

直接是使用的浏览器管理历史记录的对象, history.go(), 我有点怀疑其实浏览器原生实现 history.back() 和 history.forward() 也是用的 history.go 了, 注意实现中的 triggerListeners, 它将决定你的操作是否触发前端路由的监听器, 注意,小细节,前端路由是需要检测 URL 变化的,而 history 模式 是通过 popState 事件实现的检测变化, window.history.go(), window.history.back(), window.history.forward() 调用的时候可不会触发 popState 事件


replace

replace 实际上是调用的 replaceState(), 这个东西可能就需要 MDN 的文档来了解一下了, 友情链接, 注意一个小细节, 它名字叫做 replaceState, 但实际上它和 pushState 一样都会在浏览器历史纪录上创建一个新的历史记录项, 比如你在当前页面的控制台(你用手机看的话当我没说)调用下面这个代码

history.pushState(null, "", "d");
history.replaceState(null, "", "a");

虽然传递的 URL 非常离谱, 但是不妨碍它能够无刷运行, 它是不会发出请求的, 而且最重要的是, 它会新增两条历史记录, 假设你在 https://www.baidu.com 调用的这个, 那么你看历史记录里面其实会有两条记录, 一条 https://www.baidu.com/a 一条 https://www.baidu.com/b

除此之外, 还有一个小细节, 如果你之前没有用过 pushState() 直接上的 replaceState, 那么无论调用多少次 replaceState 都只是修改当前页面的历史记录, 而不会新增

回到 vue-router4 的实现, 在 router/src/history/html5.js 240

function replace(to: HistoryLocation, data?: HistoryState) {
  const state: StateEntry = assign(
    {},
    history.state,
    buildState(
      historyState.value.back,
      // keep back and forward entries but override current position
      to,
      historyState.value.forward,
      true
    ),
    data,
    { position: historyState.value.position }
  );

  changeLocation(to, state, true);
  currentLocation.value = to;
}

这里的 replace 做了三件事

  1. 创建状态对象, 这是 vue-router 自己的状态对象, 这个对象包含 window.history 自己的 state, 还有上一个历史记录和前一个历史记录, 以及编码时需要传递的数据
  2. 修改浏览器历史记录, 涉及到 replaceState() 的 changeLocation(), changeLocation() 很重要, push, replace 和浏览器自身的历史记录关联起来就靠它
  3. 修改 Router 实例中 URL 的值

重点分析一下 changeLocation(), 在 router/src/history/html5.js 203

function changeLocation(
    to: HistoryLocation,
    state: StateEntry,
    replace: boolean
  ): void {
    ...
    try {
      history[replace ? 'replaceState' : 'pushState'](state, '', url)
      historyState.value = state
    } catch (err) {
      ...
      location[replace ? 'replace' : 'assign'](url)
    }
  }

从这里可以看到 vue-router 里面 push 和 replace 实际上都是调用 changeLocation 修改浏览器历史记录的,而且当出现错误的时候会降级使用, window.location 的方式修改 URL (Hash 模式用的就是 window.location)


push

push 常用于栈之类的数据结构, 在 vue-router 里实际就是压入浏览器历史记录栈中的, 而在 vue-router 的源码实现中, push 和 replace 的实现是高度一致的,这也就是为什么先讲了 replace, 简简单单看一下源码

  function push(to: HistoryLocation, data?: HistoryState) {
    ...
    const currentState = assign(
      {},
      historyState.value,
      history.state as Partial<StateEntry> | null,
      {
        forward: to,
        scroll: computeScrollPosition(),
      }
    )

    changeLocation(currentState.current, currentState, true)

    const state: StateEntry = assign(
      {},
      buildState(currentLocation.value, to, null),
      { position: currentState.position + 1 },
      data
    )

    changeLocation(to, state, false)
    currentLocation.value = to
  }

和 replace 实现的目的实际上是一样的, 但是有个小细节, 它是调用了两次 changeLocation, 又一个小细节!第一次是为了更新当前页面的历史记录状态对象信息, 更新信息包括当前页面滚动信息(当我们返回上一级路由时能够直接闪现到上一次访问时的位置), 以及 forward(前一个历史记录 URL), 因为这个页面要跳转到新的页面, 第二次 changeLocation 就是创建跳转页面的历史记录状态对象了


总结

前端路由(指 vue-router), 它们修改 URL 并不是说修改了就算了, 直接修改调用 replaceState 和 pushState 就行了, vue-router 在修改路由的同时, 用来大量的精力去维护状态对象, 状态对象中存储了一个历史记录条目的信息(小细节, 这个状态对象序列化后的大小不能超过 640kb), 这个状态对象是前端路由传递信息, 维护状态, 正确导航的核心

看来 vue-router 部分源码后,其实它们的很多实现也是依据浏览器提供的原生 API 实现的,并不是毫无根据的,说白了,你自己也能用这个 API 去捣鼓一个轮子出来,但是没必要,别人造的库,嵌套的那叫一个眼花缭乱,各式各样都给你考虑好了,所以说 珍爱生命,远离造轮

作者:2分钟速写快排
链接:https://juejin.cn/post/7061622900490502151

链接: https://fly63.com/article/detial/11105

vue路由history模式_如何去除vue项目中的#

在使用vue-cli搭建的环境中,浏览器上URL地址中是存在#的,这是由于vue-router 默认 hash 模式,不难发现#的出现真的很丑陋。官网给出了如何使用history模式mode: history

vue路由传参主要的3种方式

vue中路由传参主要的3种方式:query方式(push时使用path来匹配)、params模式(push时使用name来匹配)、location预声明参数模式(push使用path来匹配,但是它跟params模式不同)

vue动态加载路由_实现vue动态加载路由器设置

我们的通用的后台管理系统中,我们会根据权限的粗细不同,会对每个角色每个权限每个资源进行控制。同样的我们也需要实现一个这样的功能。 这篇文章我将主要讲vue端的实现,关于后台接口我就不会涉及,当我接触的时候我们的后台接口是springcloud实现。

两种前端路由的实现方式

前后端分离开发模式,后端会把路由控制丢在前端,这几天再开发单页面小的项目,手动撸了个路由。前端路由实现有两种方法。HTML5 History API包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。hash + location.onhashchange

vue动态路由_vue-router通过接口请求动态生成路由的实现

在后台管理系统中,一般都会采用权限管理。路由菜单数据都会保存到数据库中,在vue-router 2.2版本新增了一个router.addRoutes(routes)方法,即可用它来实现动态路由了

HTML5 History 模式

vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

vue router 路由鉴权(非动态路由)

原本想用动态路由的思路去做,按权限加载对应路由表,但是由于权限可以交叉(比如一个人可以同时是主题管理员和数据服务管理员),导致权限路由表还是得去做判断组合。于是放弃了这个思路,索性就在beforeEach里直接判断了。

vue中路由按需加载的几种方式

使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,webpack在打包的时候会把整个路由打包成一个js文件,如果页面一多,会导致这个文件非常大,加载缓慢

vue-router 中参数传递(params,query)

query和params的区别,query相当于get请求,在页面跳转的时候,可以在地址栏看到请求参数,然而params则相当于post请求,参数不会在地址栏中显示。

Node.js的路由

当服务端接收到HTTP请求时,可以通过onRequest() 获取到url, pathname,query,及paramParams参数;为了解析这些数据需要使用url和querystring模块

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!