技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

vue-router的两种路由模式hash和history,区别以及它们的底层实现原理

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/22-vue-router的两种路由模式hash和history,区别以及它们的底层实现原理
本文目录7 个章节
  1. 路由入口与出口
  2. Router 3 历史写法与 Router 4 当前写法
  3. hash 和 history 的区别
  4. 原生 hash 路由实验
  5. 原生 History API 实验
  6. history 模式的服务器配置
  7. 官方参考

vue-router的两种路由模式hash和history,区别以及它们的底层实现原理

Vue Router 常用 hash 和 HTML5 history 两种 Web 历史模式。下面保留原文的 <router-link><router-view>、原生实验和版本对照,同时区分 Router 3 历史 API 与当前 Router 4 API。

路由入口与出口

<template>
  <router-link to="/user">用户页</router-link>
  <router-view />
</template>

<router-link> 默认渲染为带正确 href<a>,点击时由 Router 调用客户端导航(通常相当于 router.push());普通 <a href="/user"> 默认执行文档导航,可能重新加载页面。<router-view> 是显示当前匹配路由组件的出口,并不是“预渲染”。

Router 3 历史写法与 Router 4 当前写法

Vue Router 3 通过 mode 选择模式,浏览器环境默认 hash:

// Router 3 历史 API
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

Vue Router 4 不使用 mode 字符串,必须显式传入 history 工厂:

import {
  createMemoryHistory,
  createRouter,
  createWebHashHistory,
  createWebHistory
} from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  // 可替换为 createWebHashHistory() 或 createMemoryHistory()
  routes
})

hash 和 history 的区别

hash 模式 URL 形如 /#/user,fragment 不发送给服务器,因此无法配置服务器 fallback 时仍容易部署。history 模式 URL 形如 /user,更接近普通站点地址;但直接打开或刷新时服务器必须配合。

“hash 很丑、项目中很少用”是主观且绝对的判断。更合理的选择是:能配置服务器并重视规范 URL、SEO 时通常使用 history;静态托管无法配置 fallback、file:// 或无 host 场景可以选择 hash。

原生 hash 路由实验

手写 hash 路由可监听全小写的 hashchange

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>hashchange 实验</title>
</head>
<body>
  <button id="btn">修改 hash</button>
  <script>
    const btn = document.querySelector('#btn')

    btn.addEventListener('click', () => {
      location.hash = '#/user'
    })

    window.addEventListener('hashchange', (event) => {
      console.log(event.oldURL)
      console.log(event.newURL)
      console.log(location.hash)
    })
  </script>
</body>
</html>

这个实验适合解释浏览器原生机制和自制路由,也可作为 Router 3 历史背景;不能断言当前 Router 4 的 createWebHashHistory() 全部由 onhashchange 实现。Router 4 的公开契约是 history 工厂,内部实现不应作为稳定 API。

原生 History API 实验

popstate 在浏览器前进、后退或 history.go() 激活另一历史条目时触发。原生 pushState()replaceState() 本身不会自动触发它:

window.addEventListener('popstate', (event) => {
  console.log('历史条目发生切换', event.state)
})

history.pushState({ page: 'b' }, '', '/b')
history.replaceState({ page: 'c' }, '', '/c')

原文通过重写方法观察调用的实验可以保留,但它只是自制监听实验,不是 Vue Router 4 的使用方式:

const rawPushState = window.history.pushState
window.history.pushState = function (...args) {
  const result = rawPushState.apply(this, args)
  console.log('观察到 pushState')
  return result
}

const rawReplaceState = window.history.replaceState
window.history.replaceState = function (...args) {
  const result = rawReplaceState.apply(this, args)
  console.log('观察到 replaceState')
  return result
}

正确对应关系是:

  • router.push() 对应新增历史条目,底层语义类似 history.pushState()
  • router.replace() 对应替换当前条目,底层语义类似 history.replaceState()
  • router.go()router.back()router.forward() 用于遍历历史。

Vue Router 会主动维护当前路由状态,不能把所有程序化导航都归因于 popstate;重写原生方法也不会自动产生 popstate

history 模式的服务器配置

直接访问或刷新 /user 会向服务器请求该路径。服务器要把未命中真实后端接口与静态文件的前端路由请求 rewrite/fallback 到 index.html,保持浏览器 URL 不变。例如 nginx 的核心思路是:

# 真实部署中应先为静态资源和 API 配置更高优先级的 location。
location ^~ /assets/ {
  try_files $uri =404;
}

# /api/ 通常应 proxy_pass 到后端;这里的 backend 只是示意 upstream 名称。
location ^~ /api/ {
  proxy_pass http://backend;
}

# 只有前端路由请求才回退到应用入口。
location / {
  try_files $uri $uri/ /index.html;
}

不能让静态资源和 /api 请求也回退成 HTML,否则会出现资源 MIME 错误或掩盖接口 404。fallback 后还应在客户端配置 catch-all 404 路由:

{
  path: '/:pathMatch(.*)*',
  name: 'not-found',
  component: () => import('../views/NotFound.vue')
}

官方参考

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS