这篇文章主要介绍了路由的 hash 模式和 history 模式的实现。vue 中的 router 可根据不同 url 展示对应组件。hash 模式中,# 后的内容为 hash 值,变化不引起页面重载,监听 hashchange 事件加载对应组件。history 模式需阻止 a 标签默认行为,用 () 改 url 路径,监听 popstate 事件加载组件。

关联问题: hash模式有何局限 history路由咋优化 路由实现有何难点

1. 前端router是什么

在传统的html编程中,如果想要实现页面能够展示不同页面那么我们就得写很多的html页面来根据url进行展示,但是在vue项目中是一个单页应用的状态,在vue项目中只有一个index.html,所有的页面或者叫组件都是在这个html中展示,而vue中的router就可以实现根据不同url展示对应的组件。

router: 通过url路径来匹配对应的代码块的这么一套机制

router特点:

  1. 修改 url 后,页面要更新
  2. 浏览器不刷新
  3. 如何监听 url 变更

2. hash路由实现

 1const router = createRouter({
 2  history: createWebHistory(),
 3  routes: routes
 4})

当我们在vue项目中配置路由的时候,里面有一个属性叫history,这个属性有两个值其中的createWebHashHistory() 就是我们常说的hash模式,在这个模式中我们的url路径中会多加一个#除此之外与createWebHistory() 并无区别。

在简单回忆了一下router的hash是什么之后,接下来我们来根据router特点和hash模式来自己实现一下router中的hash模式,首先我们需要了解一个小知识点:

#/home 在浏览器的 url 中出现 # 号,#后面的内容会被看作是一个 hash 值,hash 值的变化不会引起页面的重新加载

在我们了解了这一个特点之后,我们就已经完成了router中的修改url之后浏览器不刷新这一特点,接下来我们只需要监听url变更,并且在修改url后页面刷新就行,接下来先给大家展示一下基础的html代码:

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4  <meta charset="UTF-8">
 5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6  <title>Document</title>
 7</head>
 8<body>
 9  <ul>
10    <li><a href="#/home">首页</a></li>
11    <li><a href="#/about">关于</a></li>
12  </ul>
13
14  <div id="app"></div>
15
16  <script>
17    
18    const routes = [
19      {
20        path: '/home',
21        component: () => {
22          return '<h2>首页页面</h2>'
23        }
24      },
25      {
26        path: '/about',
27        component: () => {
28          return '<h3>about页面</h3>'
29        }
30      },
31    ]
32  </script>
33</body>
34</html>

如果我们想要实现router剩下的两个特点,那么就需要对页面中url的变化进行监听,当页面中的url变化的时候,在页面执行对应的routes[x].component()就可以在页面展示对应的组件。这个功能我们用一个函数routerView(localHash)来实现加载对应路径组件这个功能:

 1const app = document.getElementById('app')
 2function routerView(localHash) {
 3  
 4  
 5  const index = routes.findIndex((item) => {
 6    return '#' + item.path === localHash
 7  })
 8  app.innerHTML = routes[index].component()
 9}

要想实现上述监听url功能,我们就得用官方为我们提供的'hashchange'事件,这个事件可以监听页面中hash值的变化,我们在url中用了#将后面的值变成了hash值,而不同的页面就会展示不同的url就会让hash值进行改变,这样就能通过这个事件来加载对应的组件。

 1
 2window.addEventListener('hashchange', () => {
 3  
 4  routerView(location.hash)
 5})

同时我们需要在页面完成加载时,就展示对应的组件,完成这个功能才算实现了hash路由,这个功能我们通过官方提供的'DOMContentLoaded'事件来监听页面加载完成,接下来我们来看完整代码:

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4  <meta charset="UTF-8">
 5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6  <title>Document</title>
 7</head>
 8<body>
 9  <ul>
10    <li><a href="#/home">首页</a></li>
11    <li><a href="#/about">关于</a></li>
12  </ul>
13
14  <div id="app"></div>
15
16  <script>
17    const routes = [
18      {
19        path: '/home',
20        component: () => {
21          return '<h2>首页页面</h2>'
22        }
23      },
24      {
25        path: '/about',
26        component: () => {
27          return '<h3>about页面</h3>'
28        }
29      },
30    ]
31    const app = document.getElementById('app')
32    function routerView(localHash) {
33      
34      
35      const index = routes.findIndex((item) => {
36        return '#' + item.path === localHash
37      })
38      app.innerHTML = routes[index].component()
39    }
40
41    
42    window.addEventListener('DOMContentLoaded', () => {
43      routerView(location.hash)
44    })
45    
46    window.addEventListener('hashchange', () => {
47      
48      routerView(location.hash)
49    })
50
51  </script>
52</body>
53</html>

3. history路由实现

通过上文我们知道了history路由的路径中是没有#的,这样的话我们如果变换路径的时候那就必然会引起页面的刷新,我们实现history路由的时候要避免这一点。

首先我们页面中的基础html结构同上,我们同样需要一个routerView()函数来实现对应url组件加载功能:

 1const app = document.getElementById('app')
 2function routerView() {
 3  const index = routes.findIndex(item => {
 4    return item.path === location.pathname 
 5  })
 6  app.innerHTML = routes[index].component()
 7}

接下来我们想要实现的效果是点击对应的a标签就展示对应的组件,我们在点击a标签的时候就会进行a标签的默认跳转事件,这个时候我们只需要对要实现router效果的a标签阻止它的默认行为,然后修改url路径后执行routerView()函数即可:

history.pushState() 方法可以修改url,不会带来页面刷新

 1
 2let linkList = document.querySelectorAll('a')
 3linkList.forEach(link => {
 4  link.addEventListener('click', function(e) {
 5    
 6    e.preventDefault()
 7    
 8    history.pushState(null, '', this.getAttribute('href')) 
 9    routerView()    
10  })
11})

在实现了url变更展示对应组件和页面不刷新两点功能之后,还有一个小点得注意一下,当我们点击浏览器左上角前进后退的时候,页面也要展示对应url的组件,仅靠上述代码是实现不了的,这时候我们就需要使用'popstate'事件来实现这个效果,接下来直接给大家展示完整代码:

 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4  <meta charset="UTF-8">
 5  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6  <title>Document</title>
 7</head>
 8<body>
 9  <ul>
10    <li><a href="/home">首页</a></li>
11    <li><a href="/about">关于</a></li>
12  </ul>
13
14  <div id="app"></div>
15
16  <script>
17    const routes = [
18      {
19        path: '/home',
20        component: () => {
21          return '<h2>首页页面</h2>'
22        }
23      },
24      {
25        path: '/about',
26        component: () => {
27          return '<h3>about页面</h3>'
28        }
29      },
30    ]
31
32    
33    window.addEventListener('popstate', function () {
34      routerView()
35    })
36
37    let linkList = document.querySelectorAll('a')
38    linkList.forEach(link => {
39      link.addEventListener('click', function(e) {
40        
41        e.preventDefault()  
42        
43        history.pushState(null, '', this.getAttribute('href')) 
44        routerView()    
45      })
46    })
47
48    const app = document.getElementById('app')
49    function routerView() {
50      const index = routes.findIndex(item => {
51        return item.path === location.pathname 
52      })
53      app.innerHTML = routes[index].component()
54    }
55  </script>
56</body>
57</html>

总结

hash 路由: #/home 在浏览器的 url 中出现 # 号,#后面的内容会被看作是一个 hash 值,hash 值的变化不会引起页面的重新加载,从而直接监听hashchange事件来判断要展示的对应的组件

history 路由: 首先阻止 a 标签的默认行为,然后使用 history.pushState() 方法来修改 url 路径,它是不会引起页面的重新加载的,在url变更后,读取本地的 pathname 来判断要展示的对应的组件

个人笔记记录 2021 ~ 2025