前端路由实现方式

8/16/2022 Vue

# 背景

从事前端开发的同学,在实际工作中,一定会接触过路由这个概念,同时在面试的过程中,经常会被问及关于前端路由的实现方式,面试官到底想考察什么?在现在SPA单页面模式盛行,前后端分离的背景下,我们要弄清楚路由到底是个什么玩意,它可以帮助我们加深对于前端项目线上运作的理解。

而现在我们常见的路由实现方式,主要有两种,分别是history和hash模式。

# 理解

单页面,就是指我们的服务器只有一个index.html静态文件,这个静态文件前端生成后,丢到服务器上面。用户访问的时候,就是访问这个静态页面,而静态页面中所呈现出来的所有交互,包括点击跳转,数据渲染等,都是在这个唯一的页面中完成的

# hash模式

我们都知道url是由很多部分组成,包括协议/域名/路径/query/hash等

  1. 首页:yourdomain.xxx.com/index.html/#/
  2. 商城:yourdomain.xxx.com/index.html/#/shop
  3. 购物车:yourdomain.xxx.com/index.html/#/shopping-cart
  4. 我的:yourdomain.xxx.com/index.html/#/mine

#号后面的,就是一个URL中关于hash的组成部分,可以看到,不同路由对应的hash是不一样的,但是它们都是在访问同一个静态资源index.html。我们要做的,就是如何能够监听到URL中关于hash部分发生的变化,从而做出对应的改变

其实浏览器已经暴露给我们一个现成的方法hashchange,在hash改变的时候,触发该事件。有了监听事件,且改变hash页面并不刷新,这样我们就可以在监听事件的回调函数中,执行我们展示和隐藏不同UI显示的功能,从而实现前端路由。

# 结论

  1. hash模式所有的工作都是在前端完成的,不需要后端服务的配合
  2. hash模式的实现方式就是通过监听URL中hash部分的变化,从而做3. 出对应的渲染逻辑 hash模式下,URL中会带有#,看起来不太美观

# history模式

history路由模式的实现,是要归功于HTML5提供的一个history全局对象,可以将它理解为其中包含了关于我们访问网页(历史会话)的一些信息。同时它还暴露了一些有用的方法,比如:

  1. window.history.go 可以跳转到浏览器会话历史中的指定的某一个记录页
  2. window.history.forward 指向浏览器会话历史中的下一页,跟浏览器的前进按钮相同
  3. window.history.back 返回浏览器会话历史中的上一页,跟浏览器的回退按钮功能相同
  4. window.history.pushState 可以将给定的数据压入到浏览器会话历史栈中
  5. window.history.replaceState 将当前的会话页面的url替换成指定的数据

而history路由的实现,主要就是依靠于pushState与replaceState实现的,这里我们先总结下它们的一些特点

  1. 都会改变当前页面显示的url,但都不会刷新页面
  2. pushState是压入浏览器的会话历史栈中,会使得history.length加1,而replaceState是替换当前的这条会话历史,因此不会增加history.length

既然已经能够通过pushState或replaceState实现改变URL而不刷新页面,那么是不是如果我们能够监听到改变URL这个动作,就可以实现前端渲染逻辑的处理呢?这个时候,我们还要了解一个事件处理程序popstate,先看下它的官方定义

每当激活同一文档中不同的历史记录条目时,popstate 事件就会在对应的 window 对象上触发。如果当前处于激活状态的历史记录条目是由 history.pushState() 方法创建的或者是由 history.replaceState() 方法修改的,则 popstate 事件的 state 属性包含了这个历史记录条目的 state 对象的一个拷贝。 调用 history.pushState() 或者 history.replaceState() 不会触发 popstate 事件。popstate 事件只会在浏览器某些行为下触发,比如点击后退按钮(或者在 JavaScript 中调用 history.back() 方法)。即,在同一文档的两个历史记录条目之间导航会触发该事件。

这里我用大白话总结下就是以下几点

  1. history.pushState和history.replaceState方法是不会触发popstate事件的
  2. 但是浏览器的某些行为会导致popstate,比如go、back、forward
  3. popstate事件对象中的state属性,可以理解是我们在通过history.pushState或history.replaceState方法时,传入的指定的数据

hash模式是不需要后端服务配合的。但是history模式下,如果你再跳转路由后再次刷新会得到404的错误,这个错误说白了就是浏览器会把整个地址当成一个可访问的静态资源路径进行访问,然后服务端并没有这个文件~看下面例子更好理解

所以一般情况下,我们都需要配置下nginx,告诉服务器,当我们访问的路径资源不存在的时候,默认指向静态资源index.html

# 总结

  1. 一般路由实现主要有history和hash两种方式
  2. hash的实现全部在前端,不需要后端服务器配合,兼容性好,主要是通过监听hashchange事件,处理前端业务逻辑
  3. history的实现,需要服务器做以下简单的配置,通过监听pushState及replaceState事件,处理前端业务逻辑