前端路由如何修改 URL ?

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

前端路由需要实现两个核心, 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月前
|
移动开发 前端开发 API
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
|
2月前
|
前端开发 搜索推荐 UED
解密前端路由: hash模式vs.history模式
解密前端路由: hash模式vs.history模式
|
3月前
|
前端开发 JavaScript
前端知识(十)———JavaScript 使用URL跳转传递数组对象数据类型的方法
前端知识(十)———JavaScript 使用URL跳转传递数组对象数据类型的方法
43 0
|
2月前
|
前端开发 JavaScript 关系型数据库
若依框架------后台路由数据是如何转换为前端路由信息的
若依框架------后台路由数据是如何转换为前端路由信息的
55 0
|
3月前
【超实用】Angular如何修改当前页面网页浏览器url后面?param1=xxx&param2=xxx参数(多用于通过浏览器地址参数保存用户当前操作状态的需求),实现监听url路由切换、状态变化。
【超实用】Angular如何修改当前页面网页浏览器url后面?param1=xxx&param2=xxx参数(多用于通过浏览器地址参数保存用户当前操作状态的需求),实现监听url路由切换、状态变化。
【超实用】Angular如何修改当前页面网页浏览器url后面?param1=xxx&param2=xxx参数(多用于通过浏览器地址参数保存用户当前操作状态的需求),实现监听url路由切换、状态变化。
|
1天前
|
前端开发
[牛客网-前端大挑战QD2] 获取url参数
[牛客网-前端大挑战QD2] 获取url参数
9 0
|
5月前
|
缓存 前端开发 JavaScript
【前端用法】jquery获取当前页面的URL信息
【前端用法】jquery获取当前页面的URL信息
45 0
|
2月前
|
前端开发 JavaScript API
SPA与前端路由:构建无缝体验的现代前端应用
在前端开发领域,单页面应用(SPA)和前端路由成为了构建现代、高度交互性的应用程序的重要技术。本文将探讨SPA的优势以及前端路由的实践,帮助读者更好地理解如何利用这些技术来提升用户体验和开发效率。
|
2月前
|
前端开发 JavaScript 搜索推荐
深入理解前端路由:构建现代 Web 应用的基石(下)
深入理解前端路由:构建现代 Web 应用的基石(下)
深入理解前端路由:构建现代 Web 应用的基石(下)
|
2月前
|
前端开发 API UED
探索前端开发中的单页面应用(SPA)与前端路由
在前端开发中,单页面应用(SPA)和前端路由技术扮演着重要的角色。本文将深入探讨SPA的概念、特点以及与传统多页面应用的对比,并介绍前端路由的原理、实现方式以及其在SPA中的应用。通过对这两个关键概念的详细解析,读者将更好地理解现代前端开发中的架构设计和技术选型。

相关产品

  • 云迁移中心