Vue-Router 路由模式原理

Hash 和 History 两种路由模式的实现原理与区别

问题

Vue-Router 提供了 Hash 和 History 两种路由模式,它们的实现原理是什么?有什么区别?

解答

Hash 模式

Hash 模式利用 URL 中的 # 部分,通过监听 hashchange 事件实现路由切换。

// Hash 路由简易实现
class HashRouter {
  constructor() {
    this.routes = {}
    // 监听 hash 变化
    window.addEventListener('hashchange', this.onHashChange.bind(this))
    // 页面加载时也要处理
    window.addEventListener('load', this.onHashChange.bind(this))
  }

  // 注册路由
  route(path, callback) {
    this.routes[path] = callback
  }

  // hash 变化时触发
  onHashChange() {
    // 获取 # 后面的路径,默认 '/'
    const hash = window.location.hash.slice(1) || '/'
    const callback = this.routes[hash]
    if (callback) {
      callback()
    }
  }

  // 跳转路由
  push(path) {
    window.location.hash = path
  }
}

// 使用示例
const router = new HashRouter()

router.route('/', () => {
  console.log('首页')
})

router.route('/about', () => {
  console.log('关于页')
})

// 跳转
router.push('/about') // URL 变为 http://example.com/#/about

History 模式

History 模式使用 HTML5 的 History API,通过 pushStatepopstate 事件实现。

// History 路由简易实现
class HistoryRouter {
  constructor() {
    this.routes = {}
    // 监听浏览器前进后退
    window.addEventListener('popstate', this.onPopState.bind(this))
  }

  // 注册路由
  route(path, callback) {
    this.routes[path] = callback
  }

  // popstate 触发时(前进/后退)
  onPopState() {
    const path = window.location.pathname
    const callback = this.routes[path]
    if (callback) {
      callback()
    }
  }

  // 跳转路由
  push(path) {
    // 修改 URL,不刷新页面
    window.history.pushState({}, '', path)
    // pushState 不会触发 popstate,需要手动执行回调
    const callback = this.routes[path]
    if (callback) {
      callback()
    }
  }

  // 替换当前路由
  replace(path) {
    window.history.replaceState({}, '', path)
    const callback = this.routes[path]
    if (callback) {
      callback()
    }
  }
}

// 使用示例
const router = new HistoryRouter()

router.route('/', () => {
  console.log('首页')
})

router.route('/about', () => {
  console.log('关于页')
})

// 跳转
router.push('/about') // URL 变为 http://example.com/about

两种模式对比

特性Hash 模式History 模式
URL 形式/#/path/path
服务器配置不需要需要配置回退
SEO不友好友好
兼容性IE8+IE10+
原理hashchange 事件History API

History 模式服务器配置

History 模式需要服务器支持,否则刷新页面会 404。

# Nginx 配置
location / {
  try_files $uri $uri/ /index.html;
}
// Node.js Express 配置
const express = require('express')
const history = require('connect-history-api-fallback')

const app = express()
app.use(history())
app.use(express.static('dist'))

Vue-Router 配置

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

// History 模式
const router = createRouter({
  history: createWebHistory(),
  routes: [...]
})

// Hash 模式
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...]
})

关键点

  • Hash 模式通过 hashchange 事件监听 # 后的变化,兼容性好,无需服务器配置
  • History 模式使用 pushState/replaceState 修改 URL,popstate 监听前进后退
  • pushState 不会触发 popstate,只有浏览器前进后退才会触发
  • History 模式需要服务器配置,将所有路由请求回退到 index.html
  • Hash 变化不会发送请求到服务器,History 模式的 URL 会