前端路由详解及手动实现

1. 前言

在Web开发的过程中,路由的使用是必不可少的。路由就是URL到相应处理程序的映射关系。用户在输入要访问的url后,路由会解析url中的路径,然后根据映射关系查找相应的处理函数。

前端路由不同于传统路由,它不需要服务器来进行解析,而是通过浏览器的location.hash或H5中的History API来实现的。在开发时,路由用来设定访问路径,并将路径与相应的组件映射起来,用户在访问相应的路径时,路由根据映射关系实现不同组件间的切换,整个过程都是在一个html页面中进行的,不涉及页面的跳转,也即是单页面应用。

前端路由的优势:

1. 页面刷新速度快。由于后端路由在请求一个新路径时,会重新向服务器发送请求,之后再根据服务器的相应结果重新渲染页面,这个过程会受到网络延迟的影响,而前端路由省略了整个请求过程,只是完成部分组件间的切换,因此页面的刷新速度会相对较快,用户体验相对较好。

2. 复用性强。使用前端路由,代码中的layout、css、js都可以共用,以此来减少重复加载,提供程序性能。

3. 页面状态可记录。不使用前端路由,仅通过ajax进行页面局部切换的单页应用,由于url始终保持不变,因此页面的状态是无法记录的,而前端路由很好的解决了这个问题。

前端路由的缺点:

1. 使用浏览器的前进,后退键时会重新发送请求,来获取数据,没有合理地利用缓存。

2. 不利于SEO。

3. 单页面无法记住之前滚动的位置,无法在前进、后退后记住之前的位置。

 

2. 手动实现

目前前端路由的实现方式主要有两种:location.hash 和 windows.history。

2.1 location.hash方式

hash就是URL中#后面的部分,也叫做url的锚部分,锚部分在服务器端会被自动忽略,但是在浏览器中会被location.hash获取到。使用hash方式实现前端路由主要用到onhashchang事件,这个事件可以监听url中hash值的变化,由此根据hash值的变化调用相应的处理程序。

简单实现:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        window.onload = function() {
            const view = document.getElementById('view')
            const home = document.getElementsByClassName('home')[0]
            const index = document.getElementsByClassName('index')[0]

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

            window.addEventListener('hashchange', () => {
                console.log('hash========', window.location.hash)
                const hash = window.location.hash
                if (hash === '#/index') {
                    view.innerHTML = 'Index'
                } else if (hash === '#/home') {
                    view.innerHTML = 'Home'
                }
            })
        }
    </script>
</head>
<body>
    <button class="home">home</button>
    <button class="index">index</button>
    <h1 id="view"></h1>
</body>
</html>

 

实现思路:当浏览器地址栏url的hash值发生变化时,会触发onhashchange事件,这时通过window.location.hash可以拿到当前url的hash值,此时hash值是带有#的,这里简单实现,不对其进行处理了。然后不同hash值调用相应处理函数处理,即对应的组件。这里只是简单实现,实际情况复杂的多。

 

2.2 history方式

 

浏览器窗口有一个history对象,用来保存用户操作页面的历史,我们在浏览网页时的前进后退操作都是基于这个对象来实现的。在实现前端路由的过程中主要用到了history对象的pushState()方法和replaceState()方法。这两个方法:

1. history.pushState(obj, title, url)

2. history.replaceState(obj, title, url)

这两个方法很类似,都接收三个参数:obj,title,url。obj是要插入history的实体的相关信息,它可以是一个js对象。title就是传入history实体的标题,火狐会自动忽略掉这个参数。url用来传递新的history实体的相对路径,如果是null则表示要插入的实体和当前的实体一致,没有改变。replaceState()与pushState()方法唯一的区别在于前者会将最新的实体覆盖掉而不是添加。replaceState()用来处理登录页面这些不需要记录到history中的情况时非常有用。

history的这两个方法不会触发浏览器页面的的主动更新,只是浏览器地址栏url和history对象会发生变化,当触发前进后退事件时浏览器才会触发onpopState事件并更新页面。作为参数传入的url也会受到同源策略的限制,如果出现跨域的情况会报错。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script>
        window.onload = function() {
            const a = document.querySelectorAll('a[href]')
            const view = document.getElementById('view')

            a.forEach(element => {
                element.addEventListener('click', (e) => {
                    e.preventDefault()
                    history.pushState({ content: element.getAttribute('href') }, '', '?' + element.getAttribute('href'))
                    view.innerHTML = element.getAttribute('href')
                })   
            });

            window.addEventListener('popstate', (event) => {
                console.log('popstate=======', document.location, event.state)
                view.innerHTML = event.state.content
            })
        }
    </script>
</head>
<body>
        <ul>
          <li><a href='/home'>home</a></li>
          <li><a href='/about'>about</a></li>
          <li><a href='/index'>index</a></li>
          <li><a href='/list'>list</a></li>
      
          <h1 id="view"></h1>
        </ul>
      </body>
</html>

实现思路:当想要跳转到指定url时,先通过pushState()或replaceState()改变url和history对象,然后更新视图,此时由于上面两个方法不会主动触发浏览器页面更新,因此仍然停留在当前页面。然后点击前进后退时,onpopstate事件被触发,更新视图。

 

3. 简谈路由

 

前端路由的出现要从 ajax 开始,Ajax,全称 Asynchronous JavaScript And XML,是浏览器用来实现异步加载的一种技术方案。

在 90年代初,大多数的网页都是通过直接返回 HTML 的,用户的每次更新操作都需要重新刷新页面。极其影响交互体验,随着网络的发展,迫切需要一种方案来改善这种情况。

1996年,微软首先提出 iframe 标 签,iframe 带来了异步加载和请求元素的概念,随后在 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest 的前身), 并在 IE5 通过 ActiveX 来实现了这项技术。在微软实现这个概念后,其他浏览器比如 Mozilia,Safari,Opera 相继以 XMLHttpRequest 来实现 Ajax。 ( 兼容问题从此出现)不过在 IE7 发布时,微软选择了妥协,兼容了 XMLHttpRequest 的实现。

有了 Ajax 后,用户交互就不用每次都刷新页面, 体验带来了极大的提升。但真正让这项技术发扬光大的,还是后来的Google Map,它的出现向人们展现了 Ajax 的真正魅力,释放了众多开发 人员的想象力,让其不仅仅局限于简单的数据和页面交互,为后来异步交互体验方式的繁荣发展带来了根基。而异步交互体验的更高级版本就是 SPA —— 单页应用。

单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为 了实现单页应用,所以就有了前端路由。单页应用的概念是伴随着 MVVM 出现的。最早由微软提出,然后他们在浏览器端用 `Knockoutjs` 实现。但这项技术的强大之处并未当时的开发者体会到,可能是因为 `Knockoutjs` 实现过于复杂,导致没有大面积的扩散。同样,这次接力的选手依然是 Google。Google 通过 Angularjs 将 MVVM 及单页应用发扬光大,让前端 开发者能够开发出更加大型的应用,职能变得更大了随后都是就是前端圈 开始得到了爆发式的发展,陆续出现了很多优秀的框架。

前端路由就是把不同路由对应不同的内容或页面的任务交给前端来做,之前是通过服务端根据 url 不同返回不同的页面来实现。

利用 H5 的 history.pushState 和 history.replaceState ,这两个 history 新增 的 API,为前端操控浏览器历史栈提供了可能性,这两个 API 都会操作浏览器的历史栈,而不会引起页面的刷新。 不同的是,pushState 会增加一条新的历史记录,而 replaceState 则会替换当前的历史记录。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jsonbro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值