【原理探究】前端路由hash和history

95 阅读2分钟

url的构成

https://example.com:8080/path/to/resource?param1=value1&param2=value2#section1

// 获取完整的URL https://example.com:8080/path/to/resource?param1=value1&param2=value2#section
var url = new URL('https://example.com:8080/path/to/resource?param1=value1&param2=value2#section')

var href = url.href;

// 获取协议 https:
var protocol = url.protocol;

// 获取域名 example.com
var domain = url.hostname;

// 获取端口 8080
var port = url.port;

// 获取路径 /path/to/resource
var path = url.pathname;

// 获取查询参数 ?param1=value1&param2=value2'
var queryParams = url.search; 

// 获取哈希值  #section1
var hash= url.hash; 

hash模式

在hash模式下,URL的格式为http://example.com/#/path,其中path代表不同的路由路径。当浏览器URL中的hash发生变化时,通过监听hashchange事件来触发相应的路由变化。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <div id="content"></div>
  <div id="index">首页</div>
  <div id="nav1">导航1</div>
  <div id="nav2">导航2</div>
  <script>
    
    const routers = [
      {
        path: '/index',
        content: '<h1>首页</h1>',
      },
      {
        path: '/nav1',
        content: '<h1>导航1</h2>',
      },
      {
        path: '/nav2',
        content: '<h1>导航2</h1>',
      },
    ]
    window.onhashchange = function () {
      // 在这里处理哈希变化的逻辑
      changeRouter()
    }

    index.addEventListener('click',()=>{
      window.location.hash = '/index'
    })

    nav1.addEventListener('click',()=>{
      window.location.hash = '/nav1'
    })

    nav2.addEventListener('click',()=>{
      window.location.hash = '/nav2'
    })

    function changeRouter() {
      const hash = window.location.hash
      const path = hash.replace('#', '')
      const router = routers.find((router) => router.path === path)
      if (router) {
        content.innerHTML = router.content
        return
      }

      content.innerHTML = '<h1>404</h1>'
    }

    changeRouter()
  </script>
</body>

</html>

点击按钮时,会触发window.location.hash = '/index'改变url的hash值,从而触发onhashchange 事件。在onhashchange 事件中获取hash值来改变页面的内容。

hash地址的变化并不会发送网络请求,页面的改变完全是由前端页面完成的。

history模式

Vue Router 的history模式(History Mode)是基于 HTML5 的 History API 实现的。通过调用 pushStatereplaceState 方法来修改浏览器的历史记录,并更新当前的路由状态。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <div id="content"></div>
  <div id="index">首页</div>
  <div id="nav1">导航1</div>
  <div id="nav2">导航2</div>
  <script>
    const routers = [
      {
        path: '/index',
        content: '<h1>首页</h1>',
      },
      {
        path: '/nav1',
        content: '<h1>导航1</h2>',
      },
      {
        path: '/nav2',
        content: '<h1>导航2</h1>',
      },
    ]

    function renderRoute() {
      const path = window.location.pathname
      const router = routers.find((router) => router.path === path)
      const content = document.getElementById('content')

      if (router) {
        content.innerHTML = router.content
      } else {
        content.innerHTML = '<h1>404</h1>'
      }
    }

    window.onpopstate = function (event) {
      console.log('popstate 事件触发', event.state)
      renderRoute()
    }

    index.addEventListener('click', () => {
      window.history.pushState(null, null, '/index')
      renderRoute()
    })

    nav1.addEventListener('click', () => {
      window.history.pushState(null, null, '/nav1')
      renderRoute()
    })

    nav2.addEventListener('click', () => {
      window.history.pushState(null, null, '/nav2')
      renderRoute()
    })
  </script>
</body>

</html>

pushStatereplaceState 仅仅是对浏览器的历史记录进行更改,url会被改变,但是浏览器不会进行刷新也不会向后端发送网络请求(路由没有使用懒加载),但是会触发代码内部的监听事件从而更新页面内容,单纯的进行页面的切换不需要依赖服务器。

但是随着历史记录的更改,会产出新的url,这时候进行页面的刷新,浏览器会向服务器请求新的url,导致服务器返回 404 错误。因此服务器需要正确配置以确保在访问任何路径时都返回同一个 HTML 页面,从而避免服务器返回 404 错误。