Hash 路由原理

前端路由 Hash 模式的实现原理与代码示例

问题

解释前端路由中 Hash 模式的工作原理,并实现一个简单的 Hash 路由。

解答

Hash 模式基础

Hash 模式利用 URL 中 # 后面的部分(hash 值)来实现前端路由。hash 值的变化不会触发页面刷新,也不会向服务器发送请求。

https://example.com/#/home

                   hash 部分

核心 API

// 获取当前 hash 值
window.location.hash  // '#/home'

// 设置 hash 值
window.location.hash = '#/about'

// 监听 hash 变化
window.addEventListener('hashchange', (e) => {
  console.log('旧 URL:', e.oldURL)
  console.log('新 URL:', e.newURL)
  console.log('当前 hash:', location.hash)
})

实现简易 Hash 路由

class HashRouter {
  constructor() {
    // 存储路由与回调的映射
    this.routes = {}
    // 绑定 hashchange 事件
    window.addEventListener('hashchange', this.onHashChange.bind(this))
    // 页面加载时也要处理当前 hash
    window.addEventListener('load', this.onHashChange.bind(this))
  }

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

  // hash 变化时的处理函数
  onHashChange() {
    // 获取当前 hash,去掉 # 号
    const hash = location.hash.slice(1) || '/'
    // 执行对应的回调
    const callback = this.routes[hash]
    if (callback) {
      callback()
    }
  }

  // 跳转到指定路由
  push(path) {
    location.hash = path
  }
}

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

router.route('/', () => {
  console.log('首页')
  document.body.innerHTML = '<h1>首页</h1>'
})

router.route('/about', () => {
  console.log('关于页')
  document.body.innerHTML = '<h1>关于页</h1>'
})

router.route('/user', () => {
  console.log('用户页')
  document.body.innerHTML = '<h1>用户页</h1>'
})

// 导航
router.push('/about')

完整 HTML 示例

<!DOCTYPE html>
<html>
<head>
  <title>Hash Router Demo</title>
</head>
<body>
  <nav>
    <a href="#/">首页</a>
    <a href="#/about">关于</a>
    <a href="#/user">用户</a>
  </nav>
  <div id="app"></div>

  <script>
    class HashRouter {
      constructor() {
        this.routes = {}
        window.addEventListener('hashchange', this.onHashChange.bind(this))
        window.addEventListener('load', this.onHashChange.bind(this))
      }

      route(path, callback) {
        this.routes[path] = callback
      }

      onHashChange() {
        const hash = location.hash.slice(1) || '/'
        const callback = this.routes[hash]
        if (callback) callback()
      }
    }

    const router = new HashRouter()
    const app = document.getElementById('app')

    router.route('/', () => {
      app.innerHTML = '<h1>首页内容</h1>'
    })

    router.route('/about', () => {
      app.innerHTML = '<h1>关于我们</h1>'
    })

    router.route('/user', () => {
      app.innerHTML = '<h1>用户中心</h1>'
    })
  </script>
</body>
</html>

Hash 模式 vs History 模式

特性Hash 模式History 模式
URL 形式/#/path/path
服务器配置不需要需要配置回退
兼容性IE8+IE10+
SEO较差较好

关键点

  • hash 变化不会刷新页面,不会发送请求到服务器
  • 通过 hashchange 事件监听 hash 变化
  • location.hash 获取和设置 hash 值
  • Hash 模式兼容性好,无需服务器配置
  • hash 值不会被包含在 HTTP 请求中