背景
说起前端路由,就不得再问一句有后台路由吗? 其实一开始是没有前端路由这个概念的,路由全部由后台服务器实现与控制。 当我们在浏览器地址栏中输入一个URL时,浏览器将其发送到后台服务器,服务器解析出地址并根据相关配置拼接成html返回给浏览器渲染。
我们以常用的Spring MVC为例,后台服务器配置片段如下:
|
|
上面我们配置了页面前缀prefix为“/WEB-INF/jsp/”,后缀suffix为“.jsp”。 当我们访问“http://localhost:8000/users/toList”时, 后台将分解出“/users/toList”路径,并找到相关的Controller处理。
|
|
此方法返回字符串“user/list”,再加上我们的前后缀prefix与suffix,即可得到“/WEB-INF/jsp/user/list.jsp”为我们返回给前端的页面。
页面目录结构如下:
按照返回的“/WEB-INF/jsp/user/list.jsp”,从上图中依次找到相应的页面给前端就完成了后台路由的处理。
通过上面的示例,我们了解了后台路由的处理流程。
为什么会出现前端路由呢?
从上面的流程中,我们不难发现,不同的url对应着不同的页面,当我们切换url时,浏览器会切换对应的页面,它是整体替换的。页面会出现闪烁刷新,导致用户体验不够好。 Google将Ajax发扬光大后,异步的局部刷新流行开来,但Ajax的无浏览历史记录导致浏览器的前进后退处理比较麻烦,这种情况下,用前端路由就可以很好的解决。 再加上单页面应用与MVVM的发展,前端路由逐渐成熟与流行起来。
实现
要实现前端路由,就要解决两个问题:
- 在页面不刷新的前提下实现url变化
- 捕捉到url的变化,以便执行页面替换逻辑
这两个问题都可以通过如下技术解决。一种是基于URL的frag(片段)技术,以“#”为标示,一种是基于HTML 5的History API。即:
- location.hash+hashchange事件
- history.pushState()+popState事件
hash方式
根据URL协议我们知道,服务器通常只处理整个对象,而不处理片段,浏览器不能将片段传送给服务器。浏览器从服务器获取整个资源之后,会根据片段来显示指定的资源。
- 当我们访问“http://localhost:8000/”时,我们首先打开了默认的index.html页面; 然后我们被重定向到一个新地址,比如:“http://localhost:8000/#/index”;
- 当我们访问用户列表时,通过页面location.hash事件触发,我们的URL变成“http://localhost:8000/#/users/list”,根据URL协议,#之后的内容不会发送给服务器, 对后台来说URL未变不需要它实现路由了,我们通过hashchange事件监听url变化,发送ajax请求向后台请求数据局部刷新页面,实现了页面的正确加载显示。
- 当我们访问其它页面时,比如部门列表时,就变成了“http://localhost:8000/#/depts/list”,同理。
通过上面的分析,我们实现了路由控制从后台向前端的转移,并同样完成了页面的正确加载显示。
history方式
这是HTML 5新增的方式,它可以在不刷新页面的前提下动态改变浏览器地址栏中的URL地址,动态修改页面上所显示资源。
- 当我们访问“http://localhost:8000/”时,我们首先打开了默认的index.html页面;然后我们被重定向到一个新地址, 比如:“http://localhost:8000/index”;
- 当我们访问用户列表时,通过页面history.pushState()事件触发,我们的URL变成“http://localhost:8000/users/list”,根据history API的实现,我们会修改地址栏的地址,但不会 向后台发送请求,再通过popState事件监听到了url的变化,发送ajax请求向后台请求数据之后局部刷新页面。
- 当我们访问其它页面时,也是同样的原理。
这两种方式的优缺点网上有很多,我就不再啰嗦了。
404问题
前后端分离的单页应用,我们使用history方式,开开心心地build完代码扔给后台部署之后一切正常。但测试人员反应,一旦刷新,就会报404的错误。 比如,我们在页面“http://localhost:8000/users/list”F5刷新时,就报了404错误。但我们本地开发一切正常啊?
这是怎么会事呢?
因为使用history模式的路由中,路由是虚拟的,后台并不存在相应的物理路径和文件。 比如,当刷新页面时,浏览器会向服务器请求/users/list,服务器实际会去找根目录下/users/list.html这个文件,发现找不到, 因为实际上我们的服务器并没有这样的物理路径/文件,所有内容都是通过Restful风格的接口返回数据通过前端自己渲染的,自然会报404错误。 而开发时,内置的服务器已经帮我们做了处理,我们当然发现不了。
要解决这个问题也很简单,只要将所有请求到统一转发到首页,利用首页加载的js解析地址栏路由动态请求数据刷新页面完成显示。
比如nginx这样配置:
|
|