后端路由
最初路由这个概念是后端提出来的。根据前端路由返回不同的页面,代表者是传统的MVC模式,模板引擎+node。
比如我们请求这样的一个路由
http://www.xxx.com/login
会经过这样的流程:
一言概之,路由是前端与后端进行交互的一种方式,根据不同的请求信息,返回不同的资源。根据路由的不同返回相应的页面只是其中功能之一。
随着单页面应用(SPA)的出现,情况有所不同。
单页面应用
所谓单页面应用也就是只有一张Web页面的应用,公共资源(js、css等)仅需加载一次,
页面跳转仅刷新局部资源,不会加载整个页面的资源。
与之相对比的是传统的多页面应用,也就是一个路由就是一个页面,页面间的跳转要刷新所有资源,每个公共资源(js、css等)需选择性重新加载。
随着单页面应用的出现,用户无需刷新整个页面,通过ajax局部刷新页面,即可获得页面的更新,优化了交互体验。也就是说SPA是通过js改变页面,不是通过url获取页面,页面本身的url没有发生变化。
前端路由
保证只有一个HTML页面,与用户进行交互时,为每一个局部视图匹配对应的URL,交互不会重新加载html资源。对应的浏览器上的操作如前进、后退、刷新等均通过这个URL来实现。此时,前端路由是指页面UI与URL的对应关系,即URL变化引起UI更新。
前端路由的实现
前端路由的实现,需要解决两个问题。
对于这两个问题的实现,有两种实现方案。
hash模式
在h5发布之前,hash模式是早期的解决方案。所谓hash是在url 中#及后面的字符串,如
www.baidu.com#helloworld
#helloworld就是路由上的hash。
hash的改变不会向服务器发送请求,即不会引起页面的刷新。同时通过事件hashchange,可以监听到hash的改变,浏览器的前进后退也能对其进行控制。
使用hash改变URL的方式只有以下几种:
以上几种情况改变 URL 都会触发 hashchange 事件。对此我们可以进行进行以下操作。
// todo 匹配 hash 做 dom 更新操作
function matchAndUpdate (event) {
let newURL = event.newURL; // hash 改变后的新 url
let oldURL = event.oldURL; // hash 改变前的旧 url
}
window.addEventListener('hashchange', matchAndUpdate)
history 模式
2014年后,随着H5的发布,新增了两个api,pushState 和 replaceState,通过这两个 API 可以改变 url 地址且不会发送请求,同时还有popstate事件。因此可以重新实现定义路由了。
与hash模式相比,history 模式不会URL后面增加难看的#,变得更加美观,只会更新浏览器的历史记录。
history 提供了 pushState 和 replaceState 两个方法来记录路由状态,这两个方法改变 URL 不会引起页面刷新
// 新增历史记录
history.pushState(stateObject,title,URL);
// 替换当前记录
history.repalceState(stateObject,title,URL);
//是一个属性,可以得到当前页的state信息。
history.state;
history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或a标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和a标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便
// 当用户做出浏览器动作时,比如点击后退按钮时会触发popState 事件
window.addEventListener('popState',(e)=>{
// ...具体逻辑
// e.state 是pushState(stateObject,title,URL)中的stateObject;
console.log(e.state)
})
pushState(state, title, url) 和 replaceState(state, title, url)都可以接受三个相同的参数。
根据 Mozilla Develop Network 的介绍,调用 history.pushState() 相比于直接修改 hash,存在以下优势:
history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。