上节课我们介绍了路由,简单的介绍了路由的工作流程,都是一些概念性的东西,可能会比较枯燥。这节课我们来讲一下前端路由的原理。

回顾

在开始介绍前端路由的原理之前我们来对上节课的内容来做一个简单的回顾:

  • 引入路由技术使得点击链接不跳转不刷新页面
  • 路由会修改和监听浏览器地址栏的路径,并根据路径展示相应组件
  • 路由是一个浏览器地址栏路径与函数或者组件的映射关系
  • 路由分为后端路由和前端路由

以上便是上节课的主要知识点,接下来我们来介绍一下前端路由的原理。

前端路由原理

我们之前是已经知道了路由的工作流程。但是为什么呢?为什么就能这样呢?我们来看一个小demo

image-20220113112549757

我们这个页面中有链接,也有几个按钮。那么这些东西都是干什么用的呢?其实我们这个demo就是为了给大家展示一下前端路由的工作原理。

我们都知道浏览器中有DOM,同时还有一个BOM。这个BOM自身呢带有一个历史记录,而前端路由就是依靠这个历史记录来完成他相应的工作的。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>前端路由的基石_history</title>
</head>
<body>
  <a href="https://www.lynchow.com" onclick="return push('/test1') ">push test1</a><br><br>
  <button onClick="push('/test2')">push test2</button><br><br>
  <button onClick="replace('/test3')">replace test3</button><br><br>
  <button onClick="back()">&lt;= 回退</button>
  <button onClick="forword()">前进 =&gt;</button>

  <script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
  <script type="text/javascript">
    // let history = History.createBrowserHistory() //方法一,直接使用H5推出的history身上的API
    let history = History.createHashHistory() //方法二,hash值(锚点)

    function push (path) {
      history.push(path)
      return false
    }

    function replace (path) {
      history.replace(path)
    }

    function back() {
      history.goBack()
    }

    function forword() {
      history.goForward()
    }

    history.listen((location) => {
      console.log('请求路由路径变化了', location)
    })
  </script>
</body>
</html>

我们把这个Demo的代码贴一下。我们首先做了一件事儿,定义了一个<a>,我们指定了跳转链接,而且还绑定了onclick事件。当我们点击这个链接的时候是不是就会去调用push函数?那么push函数又做了什么呢?我们在push函数中执行了history.push(path)这个history又是什么?我们在全局定义了这个变量了其实就是一个History对象。这个history最终会去操作BOM上的history

那么我们这步操作也就不难理解了,就是往历史记录中添加一条记录,这个记录是我们传入的。但是我后面还return false了,这是干什么的呢?有了这个return在加上onclick事件哪里的return这样就禁止了<a>的跳转,即便我们在<a>里面写了href属性,点击也不会跳转,只会去响应我们绑定的onclick事件。

当我们一点击就会往历史记录中去添加记录,常规来说历史记录中的最新的一条是什么?是不是我们当前正在访问的页面?那么我们往历史记录在添加了一条数据,那么是不是最新的一条就变成了我们自己添加进去的那一条了?但是我们的链接跳转是不是也被我们阻止了?那么我们历史记录最新的一条数据变了,那么浏览器地址栏的路径是不是要变?我们看一下效果。

image-20220113115037727

点击前我们浏览器中的地址是这样,那么我们点击一下push_test1再看:

image-20220113115151071

页面没有跳转,而且浏览器地址栏的路径变成了test1。我们早在说路由工作流程的时候就说,我们路由不会跳转页面,但是会去改路径,然后路由中还有两一个模块会来监测,并把路径对应的组件展示出来。那么这是怎么监测到的呢?

我们再继续接着上面的代码往下看。

在最下面我们是不是调用了history.listen方法啊?这就是监听地址栏,当路径发生变化时就会监听到,然后执行我们的回调。那我们看一下控制台中有没有输出吧:

image-20220113115954600

所以说大家有没有觉得这段代码所实现的就是一个大致的前端路由的工作流程?那么现在我们基本原理通过上面这个Demo大家基本上是知道了,但是我为啥还要再继续写其他的这些按钮呢?

我们借助这些按钮来演示一下什么是push什么是replace

我们的历史记录其实是一个栈的结构而不是普通的数组。那么什么是栈我想大家应该不陌生了吧,先进后出。其实当我们第一次打开我们的Demo的时候,历史记录中已经添加了一条记录了其实。当我们点击了push_test1链接时,就会在栈中压入一条数据,那条数据就是我们传入的test1

那么如果我们点击了push test2按钮的话是不是就又在栈中压入一条数据就是我们传入的test2?那么根据栈的原则,后进先出,我们最后一次入栈的数据一定就是我们正在看的页面。当我们点击了后退的话,就会把栈顶的数据出栈,那么之前一次浏览的页面就会展示出来。那么我们验证一下呗。

image-20220113121109604

最一开始,我们什么都还没点,是不是没有可以后退的

image-20220113121217524

当我们点击了push_test1链接后,现在我们的后退键是不是就亮起来了?这会儿我们就可以后退了。

image-20220113121429206

我们再点击push test2按钮之后,我们的路径是不是变成了test2?这个时候我们点击后退的话是退回哪?

image-20220113121536249

是不是会到了test1

image-20220113121610308

再点一次后退,是不是回到了初始页面?这就是push,那么什么是replace呢?

当我们打开初始页面,历史记录里面是不是已经有了一条记录,就是我们初始页面的那条记录,如果我们push的话,是不是在栈中压入新数据?这时候栈里是不是有两条数据?但是如果我们这个时候点击了replace test3按钮的话,会是什么样的一种情况呢?这样的话就会直接把栈顶的数据给替换掉。因为我们这个按钮用的replace方法而不是push了。所以说不会生成新的数据而是会替换原来栈顶的数据。那么我们现在点击后退是会回到哪里?还会回到test1吗?肯定不会,因为test1已经不在了,我们会直接回到初始页面。

总结

  • 前端路由要依赖BOM上的history对象
  • history是一个栈的结构
  • history可以pushreplace

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/unstand_frontend_router/