一、概念:路由
不管是前端还是后端,都有涉及到路由的概念,个人觉得这个描述最为恰当,路由就是URL 到函数的映射。因此这里的函数可能是前端也可能是后端,由函数决定返回具体的页面或者数据。
二、router和route的区别
在说前、后端路由之前,我们需要明确这两者的区别。
route就是一条路由,它将一个URL路径和一个函数进行映射,例如:
/users -> getAllUsers()
/users/count -> getUsersCount()
这就是两条路由,当访问 /users 的时候,会执行 getAllUsers() 函数;当访问 /users/count 的时候,会执行 getUsersCount() 函数。
而 router 可以理解为一个容器,或者说一种机制,它管理了一组 route。简单来说,route 只是进行了URL和函数的映射,而在当接收到一个URL之后,去路由映射表中查找相应的函数,这个过程是由 router 来处理的。
三、后端路由
虽然后端路由不是我们今天要说的重点,但是这也路由的发展阶段。
后端路由又可称之为服务器端路由,因为对于服务器来说,当接收到客户端发来的HTTP请求,就会根据所请求的相应URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。
对于最简单的静态资源服务器,可以认为,所有URL的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理,然后根据这些读取的数据。在服务器端就使用相应的模板来对页面进行渲染,渲染后,再返回渲染完毕的页面。
这种方式在早期的前端开发中非常普遍,它的优缺点如下:
好处:安全性好,SEO 好。
缺点:加大服务器的压力,不利于用户体验,代码冗合。
四、前端路由
对于前端路由,这个时候路由的对应的映射函数通常对应 DOM 元素的显示和隐藏。我们访问不同的路由,就会显示不同的页面组件,这个时候我们不用刷新页面就可以看到不同的页面产生,这就是前端路由的原理。
前端路由更多用在单页应用上, 也就是SPA, 因为单页应用, 基本上都是前后端分离的, 后端自然也就不会给前端提供路由
那么如何实现前端路由呢,现在主要方案有 2 个,一个是基于 Hash,另外一个是基于 HTML5 新增的 History API。
1、hash
我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash,大型框架的路由系统大多都是哈希实现的。
同样我们需要一个根据监听哈希变化触发的事件 —— hashchange 事件
我们用 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。
2、History API
这里不细说每一个 API 的用法,重点说其中的两个新增的API history.pushState 和history.replaceState
这两个 API 都接收三个参数,分别是
>1. 状态对象(state object) —
一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
>2. 标题(title) —
FireFox浏览器目前会忽略该参数,虽然以后可能会用上。考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也可以传入一个简短的标题,标明将要进入的状态。
>3.地址(URL) —
新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。
相同之处是两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。
不同之处在于,pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录。
前端路由同样也存在缺陷:使用浏览器的前进,后退键时会重新发送请求,
来获取数据,没有合理地利用缓存。
参考
1、https://zhuanlan.zhihu.com/p/88895539
2、https://github.com/joeyguo/blog/issues/2
3、https://zhuanlan.zhihu.com/p/24814675