前端路由的理解及实现它的两种方法

本文介绍了前端路由的起源、作用,详细阐述了在单页应用中为何需要前端路由,并对比了传统前端应用。接着,文章通过实例讲解了前端路由的两种实现方式:hash模式和history模式,包括它们的工作原理和实现细节,帮助读者理解并掌握前端路由的基本概念和技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前端本无路由,用的人多了,便有了前端路由。路由这个概念最先是后端出现的。前端路由的出现要从 ajax 开始,这里就不细说了,有兴趣的小伙伴可以去深入了解。接下来,让我们来了解什么是前端路由,并在将来遇到这类问题时,能够精炼简洁的回答出来。

什么是前端路由?

  • 简单说就是前端控制页面跳转,而不需要向后端去请求
  • 在前端单页应用中,路由描述的是URL和UI(页面)的映射关系

为什么会出现前端路由

以前传统的前端都是一个URL对应一个页面,所以不存在这个问题。随着SPA(single-page application)即单页应用的发展,组件的变化和更新不再对应着URL变化了。但是我们又需要这种对应关系(比如通过一个URL直接访问一个 SPA 应用的子视图),因为单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。我们急需一个工具专门负责维护组件状态与页面URL之间的对应关系,这就是前端路由的作用所在。

单页应用的优缺点

优点:用户体验好,不需要每次都从服务器全部获取,快速展现给用户
缺点:使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。单页面无法记住之前滚动的位置,无法在前进,后退的时候记住滚动的位置

如何实现前端路由

实现前端路由,我们要解决两个问题:

  1. 改变URL,页面不刷新
  2. 检测URL变化,进行页面结构改变

了解我们要做什么之后,接下来我们用这个思路来实现两个常用的前端路由方案

hash 模式

实现原理:

  1. hash在路由方面就是指在url后面的#部分。
  2. 在页面url后面加上"#"号后,再拼接其他值,地址栏回车后,页面不会进行跳转,这里就做到了改变url,不刷新页面。
  3. 接下来的问题就是检测url的变化进行页面结构改变。
    我们需要知道

浏览器地址上 “#” 后面的变化,是可以被监听的,它为我们提供了原生监听事件 hashchange。而且每次触发hashchange事件时,可以通过 location.hash 拿到当前浏览器地址的 hash 值

hashchange事件:

1、当URL的片段标识符更改时,将触发hashchange事件(跟在#符号后面的URL部分,包括#符号)
2、hashchange事件触发时,事件对象会有hash改变前的URL(oldURL)和hash改变后的URL(newURL)两个属性:window.addEventListener(‘hashchange’,function(e) { console.log(e.oldURL); console.log(e.newURL) },false);

了解完这些之后,我们来实现一个hash模式的简易路由:

<!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>hash 实现</title>
</head>
<body>
  <div class="page">
    <ul>
      <!-- 两个页面 -->
      <li>
        <a href="#/home">首页</a>
      </li>
      <li>
        <a href="#/about">关于</a>
      </li>
    </ul>
  </div>
  
  <!-- 将对应url的结构放到这个dom结构里面 -->
  <div id="routerView"></div>
  
  <script>
    // 拿到routerView的dom结构
    let routerView = document.querySelector('#routerView')
    
    // 监听路由变化
    window.addEventListener('hashchange',onHashChange

    // 页面初次加载或监听页面初次渲染完成任选其一就好
    
    // 页面初次加载执行一次hash监听
    // window.onload = function(){
    //   onHashChange()
    // } 
    
    // 监听页面的初次渲染完成
    window.addEventListener('DOMContentLoaded',onHashChange)
    
    function onHashChange(){
      // hashchange事件触发可以用location.hash拿到当前浏览器地址的 hash 值根据不同路径展示不同的内容
      console.log(location.hash);
      switch(location.hash){
        case '#/home':
          routerView.innerHTML = '这是首页页面';
          return 
        case '#/about':
          routerView.innerHTML = '这是关于页面';;
          return 
        default:
          return 
      }
    }  
  </script>
</body>
</html>

想看效果的小伙伴可以去试试,这里页面不会刷新。
总结:因为hash的改变不会引起页面刷新, 所以在URL后面拼接hash值,再通过监听hashchange事件检测到url的变化,从而去显示不同的页面(dom)结构,这就是hash的实现。

不知道小伙伴有没有注意一个细节,在创建vue项目选择路由模式时,它不会问你要不要创建hash模式,而是直接问你要不要用history模式。从这里我们可以看出,history模式比hash模式更加优秀。那为什么hash模式不如history模式呢,因为hash模式始终要在url后加一个#号,而history方式不需要,更加简洁,不影响地址栏。接下来我们来看看history模式。

history 模式

我们首先需要了解:

  1. html 提供了一个history对象,该对象提供了pushState 和 replaceState 两个方法
  2. 可以使用 popstate 事件来监听 url 的变化。
  3. 可以使用history.pushState()和history.replaceState()顶替掉 href 的事件,且这两个事件会改变url。
  4. history.pushState()或history.replaceState() 不会触发 popstate 事件,故需要手动触发页面渲染。

MDN阅读文档得知:

history.pushState() 方法向当前浏览器会话的历史堆栈中添加一个状态(state),也就是向历史记录中追加一条记录history.pushState()

history.replaceState()方法修改当前历史记录实体,如果你想更新当前的state对象或者当前历史实体的URL来响应用户的的动作的话这个方法将会非常有用,意思就是可以替换当前页在历史记录中的信息history.replaceState()

语法:

history.pushState(state, title[, url])
history.replaceState(stateObj, title[, url])

知道这些后,我们来实现一下 history 模式

<!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>history 实现</title>
</head>
<body>
  <div class="page">
    <ul>
      <!-- 希望能干掉这两个页面的自动跳转,下面实现 -->
      <li><a href="/home">首页</a></li>
      <li><a href="/about">关于</a></li>
    </ul>
    <div id="routerView"></div>
    <script>
      let routerView = document.getElementById('routerView')
      
      // 点击浏览器的前进后退按钮使URL发生变化,靠监听popstate渲染正确的页面
      window.addEventListener('popstate',onPopState)

      // 页面初次渲染
      window.addEventListener('DOMContentLoaded',onLoad)
       
      // 为全部 a 标签安上点击事件
      function onLoad(){
        // 页面初始加载
        onPopState()
        // 取到相应的具有href属性的a标签数组
        let linkList = document.querySelectorAll('.page a[href]')
        // 手动给a标签安排点击事件
        linkList.forEach(el=>{
          el.addEventListener( 'click' , function(e){
          
            // 阻止a标签的默认事件
            e.preventDefault()  
            
            // 手动让url发生改变,且页面不刷新
            // getAttribute获取当前a标签的href属性
            history.pushState(null,'',el.getAttribute('href')) 
            // 手动调用onPopState()检测url的变化
            onPopState()
          })
        })
      }
      
      // 检测url改变展示不同内容
      function onPopState() {
        // Location.pathName 取域名后面部分
        switch (location.pathname) {
          case '/home':
            routerView.innerHTML = '<h2>Home页面</h2>';
            return
          case '/about': 
            routerView.innerHTML = '<h2>About页面</h2>';
            return
          default:
            return
        }
      }
    </script>
  </div>
</body>
</html>

总结

本文简单概述了前端路由是什么以及如何简单实现前端路由。当然vue-router、react-router的实现远比这两种实现复杂,读者会继续学习剖析,也希望这篇文章能给需要的小伙伴带来帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值