站点工具

用户工具


# 当问到VueRouter两种模式和原理时

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

### Hash模式

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

```javascript <nav>

<a class="link" href="/">home</a>
<a class="link" href="/a">a</a>
<a class="link" href="/c">c</a>

</nav> <section id=“home”>

<h1>home</h1>
<p>This is home page</p>

</section> <section id=“a”>

<h1>a</h1>
<p>This is a page</p>

</section> <section id=“default”>

<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')
    },
  ]
})
以上代码的原理是: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 id="home">
  <h1>home</h1>
  <p>This is home page</p>
</section>
<section id="a">
  <h1>a</h1>
  <p>This is a page</p>
</section>
<section id="default">
  <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](https://link.zhihu.com/?target=http%3A//localhost%3A8080/) ,当用户点击链接时,url变成 [http://localhost:8080/a](https://link.zhihu.com/?target=http%3A//localhost%3A8080/a) 。 用户确实能看到/a 路径相对应的页面\(区域/组件\),但当在该链接下刷新时,向服务端发送请求的的路径是 [http://localhost:8080/a](https://link.zhihu.com/?target=http%3A//localhost%3A8080/a) ,而服务端并未做/a的配置,会导致请求失败。所以使用history模式需要服务端做响应的配置。

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

  

  

推荐一门好课 [Vue3从入门到熟练 \- 写代码啦!](https://link.zhihu.com/?target=https%3A//xiedaimala.com/courses/ca345cb6-0a9a-4e35-ba7d-3b6240434ff9/random/1253d66b6c%23/common)

> 饥人谷一直致力于培养有灵魂的编程者,打造专业有爱的国内前端技术圈子。如造梦师一般帮助近千名不甘寂寞的追梦人把编程梦变为现实,他们以饥人谷为起点,足迹遍布包括facebook、阿里巴巴、百度、网易、京东、今日头条、大众美团、饿了么、ofo在内的国内外大小企业。 了解培训课程:加微信 [xiedaimala03](https://wiki.jirengu.com/lib/exe/fetch.php?w=400&tok=5c45ca&media=%E9%A5%A5%E4%BA%BA%E8%B0%B7%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A1%E5%B0%8F%E5%8A%A9%E7%90%86black.png),官网:https://jirengu.com
若愚 · 2023/02/08 19:02 · 当问到vuerouter两种模式和原理时.1675854147.txt.gz