前端路由原理

后端路由
最初路由这个概念是后端提出来的。根据前端路由返回不同的页面,代表者是传统的MVC模式,模板引擎+node。
比如我们请求这样的一个路由

http://www.xxx.com/login

会经过这样的流程:

  1. 浏览器发出请求
  2. 服务器解析请求,解析请求的URL
  3. 服务器根据配置的路由信息,返回相应的信息
  4. 浏览器根据服务器返回的信息决定如何解析数据

一言概之,路由是前端与后端进行交互的一种方式,根据不同的请求信息,返回不同的资源。根据路由的不同返回相应的页面只是其中功能之一。

随着单页面应用(SPA)的出现,情况有所不同。

单页面应用
所谓单页面应用也就是只有一张Web页面的应用,公共资源(js、css等)仅需加载一次,
页面跳转仅刷新局部资源,不会加载整个页面的资源。
前端路由原理_第1张图片
与之相对比的是传统的多页面应用,也就是一个路由就是一个页面,页面间的跳转要刷新所有资源,每个公共资源(js、css等)需选择性重新加载。
前端路由原理_第2张图片
随着单页面应用的出现,用户无需刷新整个页面,通过ajax局部刷新页面,即可获得页面的更新,优化了交互体验。也就是说SPA是通过js改变页面,不是通过url获取页面,页面本身的url没有发生变化。

前端路由
保证只有一个HTML页面,与用户进行交互时,为每一个局部视图匹配对应的URL,交互不会重新加载html资源。对应的浏览器上的操作如前进、后退、刷新等均通过这个URL来实现。此时,前端路由是指页面UI与URL的对应关系,即URL变化引起UI更新。

前端路由的实现
前端路由的实现,需要解决两个问题。

  1. 如何URL的更新不会向服务器发送请求
  2. 如何检测URL的变化

对于这两个问题的实现,有两种实现方案。

hash模式
在h5发布之前,hash模式是早期的解决方案。所谓hash是在url 中#及后面的字符串,如

www.baidu.com#helloworld

#helloworld就是路由上的hash。
hash的改变不会向服务器发送请求,即不会引起页面的刷新。同时通过事件hashchange,可以监听到hash的改变,浏览器的前进后退也能对其进行控制。
使用hash改变URL的方式只有以下几种:

  1. 通过浏览器前进后退改变 URL
  2. 通过标签改变 URL
  3. 过window.location改变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,存在以下优势:

  • pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 #后面的部分,因此只能设置与当前 URL 同文档的 URL;
  • pushState() 设置的新 URL 可以与当前 URL一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
  • pushState() 通过stateObject 参数可以添加任意类型的数据到记录中;而 hash 只可添加短字符串;
  • pushState() 可额外设置title 属性供后续使用。

history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要通过 URL 向后端发起 HTTP 请求时,两者的差异就来了。尤其在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候。

  • hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
  • history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误。

你可能感兴趣的:(html)