VueRouter 两种模式和原理

发布于 2023-09-10 10:25:25 字数 4756 浏览 29 评论 0

使用 Vue 必然会用到 VueRouter,也会遇到 VueRouter 的 Hash 和 History 两种模式。可是如果当问到 VueRouter 这两种模式分别是什么原理,不一定所有人都回答的上来。 这篇文章我们分别用原生 JS 实现两种模式的简易 Router,揭开 VueRouter 的面纱。

Hash 模式

先看代码,下面再对代码做个讲解

<nav>
  <a class="link" href="/">home</a>
  <a class="link" href="/a">a</a>
  <a class="link" href="/c">c</a>
</nav>
<section>
  <h1>home</h1>
  <p>This is home page</p>
</section>
<section>
  <h1>a</h1>
  <p>This is a page</p>
</section>
<section>
  <h1>404</h1>
  <p>404</p>
</section>

<script>

  class Router {
    constructor({ routes }) {
      this.routes = routes     
      window.onhashchange = () => this.setPage()     
      document.querySelectorAll('.link').forEach(node => {    
        node.addEventListener('click', (e) => {
          location.hash = node.pathname    // 当点击链接时,修改当前 URl 上的 Hash
          e.preventDefault()
        })
      })
      this.setPage()              // 初始时设置要展示的页面
    }

    setPage() {   
      this.routes.forEach(route => route.component.style.display = "none")  //先隐藏所有部分
      //从当前 URL 的 hash 从配置表中查到要展示的部分,如果不存在,使用默认部分
      let route = this.routes.find(route => '#' + route.path ===  location.hash) || this.routes[this.routes.length - 1]
      route.component.style.display = "block"
    }

  }

  new Router({
    routes: [                  
      {
        path: '/',
        component: document.querySelector('#home')
      },
      {
        path: '/a',
        component: document.querySelector('#a')
      },
      {
        component: document.querySelector('#default')
      },
    ]
  })

</script>

以上代码的原理是:1. 先隐藏应用里的所有页面; 2. 根据 URL 的 Hash,和之前配置的路由表,来确定要展示的特定区域; 3. 监听 URL hash 的变化,当用户通过点击链接触发 hash 变化时,重新设置页面上要展示的区域。

本质上是使用 onhashchange 监听 hash 的变化,用户通过点击触发 hash 的变化,hash 发生改变时重新根据新 hash 和路由配置表展示特定区域。

History 模式

先看 history 模式的案例,和 hash 模式相比,只有注释 1、2、3 部分有差别。

<nav>
  <a class="link" href="/">home</a>
  <a class="link" href="/a">a</a>
  <a class="link" href="/c">c</a>
</nav>
<section>
  <h1>home</h1>
  <p>This is home page</p>
</section>
<section>
  <h1>a</h1>
  <p>This is a page</p>
</section>
<section>
  <h1>404</h1>
  <p>404</p>
</section>

<script>
  class Router {
    constructor({ mode, routes }) {
      this.routes = routes
      window.onpopstate = (e) => this.setPage(location.pathname)  //1
      
      document.querySelectorAll('.link').forEach(node => {
        node.addEventListener('click', (e) => {
          this.setPage(node.pathname)                            //2
          e.preventDefault()
        })
      })
      this.setPage(location.pathname)
    }

    setPage(path) {
      history.pushState({}, "", path)                             //3
      this.routes.forEach(route => route.component.style.display = "none")
      let route = this.routes.find(route => route.path === path) || this.routes[this.routes.length - 1]
      route.component.style.display = "block"
    }
  }

  new Router({
    routes: [
      {
        path: '/',
        component: document.querySelector('#home')
      },
      {
        path: '/a',
        component: document.querySelector('#a')
      },
      {
        component: document.querySelector('#default')
      },
    ]
  })
</script>

以上代码中,history.pushState 用来修改浏览器地址栏展示的路径,onpopstate 用来监听用户浏览器的前进、后退事件。

当用户点击链接时,获取点击链接的路径,调用 setPage 渲染路由配置表中和路径匹配的部分,同时通过使用 pushState 修改浏览器的展示的路径。当监听到浏览器前进、回退事件时,根据新的 pathname 重新调用 setPage 设置渲染区域。

关注点

使用 history 模式时,用户点击页面链接确实能“跳转”到不同页面,也会发现浏览器地址栏路径发生了视觉上的变化。但在子页面刷新时如果后端未做配置会出现问题。

比如 初始 url 是 http://localhost:8080 ,当用户点击链接时,url 变成 http://localhost:8080/a 。 用户确实能看到/a 路径相对应的页面(区域/组件),但当在该链接下刷新时,向服务端发送请求的的路径是 http://localhost:8080/a ,而服务端并未做/a 的配置,会导致请求失败。所以使用 history 模式需要服务端做响应的配置。

使用 hash 模式不存在以上问题,比如初始 url 是 http://localhost:8080 ,当用户点击链接时 url 变成 http://localhost:8080#/a 。用户能看到配置的/a 对应的新页面。当刷新时,向服务端发送的请求路径还是 / ,服务端会正常返回当前 html 的所有内容。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

私藏温柔

暂无简介

0 文章
0 评论
513 人气
更多

推荐作者

lioqio

文章 0 评论 0

Single

文章 0 评论 0

禾厶谷欠

文章 0 评论 0

alipaysp_2zg8elfGgC

文章 0 评论 0

qq_N6d4X7

文章 0 评论 0

放低过去

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文