Vue Router 的 history 模式

注:原文标题写为"Vuex",实际内容为 Vue Router 的 history 模式,特此说明。

Vue Router 的 history 模式充分利用了 history.pushState API 来完成 URL 跳转而不需要重新加载页面。

1. 两种 history 模式

Hash 模式(createWebHashHistory())

import { createRouter, createWebHashHistory } from 'vue-router';

const router = createRouter({
  history: createWebHashHistory(),
  routes: [/* ... */]
});

在内部传递的 URL 之前使用哈希字符(#)。这部分 URL 从未被发送到服务器,所以不需要在服务器层面进行特殊处理。但对 SEO 不友好。

HTML5 模式(createWebHistory())

import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory(),
  routes: [/* ... */]
});

URL 看起来"正常",如 https://example.com/user/id。但如果没有适当的服务器配置,用户直接访问 https://example.com/user/id 会得到 404。

解决办法:在服务器上添加一个简单的回退路由——URL 不匹配任何静态资源时,返回 index.html。

注意:开启 history 模式后服务器对所有路径都返回 index.html,会"吞掉" 404。要在 Vue 应用里覆盖所有路由并显式提供 404 组件:

const router = new VueRouter({
  mode: 'history',
  routes: [{ path: '*', component: NotFoundComponent }]
});

如果用 Node.js 后台,可通过服务端路由匹配 URL,未匹配到时返回 404 实现 fallback。

2. 实现原理

实现一个页面操作不会整页刷新、且能在浏览器前进/后退时正确响应的网站:

  • 通过 pushState + ajax 实现浏览器无刷新前进/后退。
  • 一次 ajax 调用成功后,将一条 state 记录加入 history 对象。
  • 一条 state 记录包含 url、title、content 属性;在 popstate 事件中可获取。
  • 用 content 传递数据。
  • 通过 window.onpopstate 事件监听,响应浏览器前进/后退。

两个问题:

  1. 打开首页时没有记录:用 replaceState 把首页的记录替换掉。
  2. 页面刷新仍会向服务端请求:把请求的 URL 经后端重定向到一个页面。

3. History API

window.history.back();     // 后退
window.history.go();       // 前进/后退指定步数
window.history.forward();  // 前进

添加 / 修改历史记录条目

HTML5 引入 history.pushState() 和 history.replaceState(),配合 window.onpopstate 使用。

history.pushState(stateObj, 'name', 'path');
window.onpopstate = function (e) { /* ... */ };

history.pushState() 可以改变 referrer:在该 state 后创建的 XMLHttpRequest 对象的 referrer 都会随之改变。

举例

window.onpopstate = function (e) { alert(2); };

const stateObj = { foo: 'bar' };
history.pushState(stateObj, 'page 2', 'bar.html');

地址栏变为 http://mozilla.org/bar.html,但不会加载 bar.html,甚至不会检查它是否存在。

  • 用户在 bar.html 点击"后退"按钮 → 触发 alert(2)。
  • 用户在 bar.html 跳转到外部页面后又"后退" → 回到 bar.html,history.state 含 stateObj 拷贝,因为页面重新加载了 popstate 不会触发。
  • 再按"后退" → URL 变为 foo.html,触发新的 popstate,state 为 null。

来源整理自:我的有道云笔记