Open fengshi123 opened 5 years ago
前端三大框架 Angular、React、Vue ,它们的路由解决方案 angular/router、react-router、vue-router 都是基于前端路由原理进行封装实现的,因此将前端路由原理进行了解和掌握是很有必要的,因为我们再使用的过程中也难免会遇到一些坑,一旦我们掌握了它的实现原理,那么就能在开发中对路由的使用更加游刃有余。
Angular
React
Vue
angular/router
react-router
vue-router
路由的概念起源于服务端,在以前前后端不分离的时候,由后端来控制路由,当接收到客户端发来的 HTTP 请求,就会根据所请求的相应 URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有 URL 的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理等等。然后根据这些读取的数据,在服务器端就使用相应的模板来对页面进行渲染后,再返回渲染完毕的页面。它的好处与缺点非常明显:
HTTP
URL
SEO
也正是由于后端路由还存在着自己的不足,前端路由才有了自己的发展空间。对于前端路由来说,路由的映射函数通常是进行一些 DOM 的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有以下两种实现方案:
DOM
Hash
History
当然,前端路由也存在缺陷:使用浏览器的前进,后退键时会重新发送请求,来获取数据,没有合理地利用缓存。但总的来说,现在前端路由已经是实现路由的主要方式了,前端三大框架 Angular、React、Vue ,它们的路由解决方案 angular/router、react-router、vue-router 都是基于前端路由进行开发的,因此将前端路由进行了解和 掌握是很有必要的,下面我们分别对两种常见的前端路由模式 Hash 和 History 进行讲解。
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理也很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':
location.hash
'#search'
https://www.word.com#search
此外,hash 也存在下面几个特性:
hash
我们可以使用 hashchange 事件来监听 hash 的变化。
hashchange
我们可以通过两种方式触发 hash 变化,一种是通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 就会发生改变,也就会触发 hashchange 事件了:
a
href
<a href="#search">search</a>
还有一种方式就是直接使用 JavaScript来对 loaction.hash 进行赋值,从而改变 URL,触发 hashchange 事件:
JavaScript
loaction.hash
location.hash="#search"
以下实现我们采用第2种方式来实现。
我们先定义一个父类 BaseRouter,用于实现 Hash 路由和 History 路由的一些共有方法;
BaseRouter
export class BaseRouter { // list 表示路由表 constructor(list) { this.list = list; } // 页面渲染函数 render(state) { let ele = this.list.find(ele => ele.path === state); ele = ele ? ele : this.list.find(ele => ele.path === '*'); ELEMENT.innerText = ele.component; } }
我们简单实现了 push 压入功能、go 前进/后退功能,相关代码的注释都已经标上,简单易懂,就不在一 一介绍,参见如下:
push
go
export class HashRouter extends BaseRouter { constructor(list) { super(list); this.handler(); // 监听 hashchange 事件 window.addEventListener('hashchange', e => { this.handler(); }); } // hash 改变时,重新渲染页面 handler() { this.render(this.getState()); } // 获取 hash 值 getState() { const hash = window.location.hash; return hash ? hash.slice(1) : '/'; } // push 新的页面 push(path) { window.location.hash = path; } // 获取 默认页 url getUrl(path) { const href = window.location.href; const i = href.indexOf('#'); const base = i >= 0 ? href.slice(0, i) : href; return base +'#'+ path; } // 替换页面 replace(path) { window.location.replace(this.getUrl(path)); } // 前进 or 后退浏览历史 go(n) { window.history.go(n); } }
Hash 模式的路由实现例子的效果图如下所示:
前面的 hash 虽然也很不错,但使用时都需要加上 #,并不是很美观。因此到了 HTML5,又提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
HTML5
History API
API
history.pushState()
history.repalceState()
window.history.pushState(null, null, path); window.history.replaceState(null, null, path);
此外,history 存在下面几个特性:
history
pushState
repalceState
title
null
popstate
url
history.replaceState()
我们同样简单实现了 push 压入功能、go 前进/后退功能,相关代码的注释都已经标上,简单易懂,就不在一 一介绍,参见如下:
export class HistoryRouter extends BaseRouter { constructor(list) { super(list); this.handler(); // 监听 popstate 事件 window.addEventListener('popstate', e => { console.log('触发 popstate。。。'); this.handler(); }); } // 渲染页面 handler() { this.render(this.getState()); } // 获取 url getState() { const path = window.location.pathname; return path ? path : '/'; } // push 页面 push(path) { history.pushState(null, null, path); this.handler(); } // replace 页面 replace(path) { history.replaceState(null, null, path); this.handler(); } // 前进 or 后退浏览历史 go(n) { window.history.go(n); } }
History 模式的路由实现例子的效果图如下所示:
本文我们大致介绍了什么路由、前端路由的源起、以及分析了两种前端路由:Hash 模式和 History 模式的原理以及简单功能实现,文中例子的代码实现已经放到 github 上面:https://github.com/fengshi123/router-example 。通过本文对前端路由原理的掌握,这时你就可以基础原理基础去阅读 vue-router 和 react-router 的源码实现了。
github地址为:https://github.com/fengshi123/blog ,上面汇总了作者所有的博客文章,如果喜欢或者有所启发,请帮忙给个 star ~,对作者也是一种鼓励。
1、浅谈前端路由:https://www.jianshu.com/p/d2aa8fb951e4
2、前端路由:https://segmentfault.com/a/1190000017853553
good
前言
前端三大框架
Angular
、React
、Vue
,它们的路由解决方案angular/router
、react-router
、vue-router
都是基于前端路由原理进行封装实现的,因此将前端路由原理进行了解和掌握是很有必要的,因为我们再使用的过程中也难免会遇到一些坑,一旦我们掌握了它的实现原理,那么就能在开发中对路由的使用更加游刃有余。一、什么是路由?
路由的概念起源于服务端,在以前前后端不分离的时候,由后端来控制路由,当接收到客户端发来的
HTTP
请求,就会根据所请求的相应URL
,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有URL
的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理等等。然后根据这些读取的数据,在服务器端就使用相应的模板来对页面进行渲染后,再返回渲染完毕的页面。它的好处与缺点非常明显:SEO
好; 也正是由于后端路由还存在着自己的不足,前端路由才有了自己的发展空间。对于前端路由来说,路由的映射函数通常是进行一些
DOM
的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有以下两种实现方案:Hash
History
当然,前端路由也存在缺陷:使用浏览器的前进,后退键时会重新发送请求,来获取数据,没有合理地利用缓存。但总的来说,现在前端路由已经是实现路由的主要方式了,前端三大框架
Angular
、React
、Vue
,它们的路由解决方案angular/router
、react-router
、vue-router
都是基于前端路由进行开发的,因此将前端路由进行了解和 掌握是很有必要的,下面我们分别对两种常见的前端路由模式Hash
和History
进行讲解。二、前端路由的两种实现
2.1、Hash 模式
2.1.1、原理
早期的前端路由的实现就是基于
location.hash
来实现的。其实现原理也很简单,location.hash
的值就是URL
中 # 后面的内容。比如下面这个网站,它的location.hash
的值为'#search'
:此外,
hash
也存在下面几个特性:URL
中hash
值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash
部分不会被发送。hash
值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash
的切换。我们可以使用
hashchange
事件来监听hash
的变化。我们可以通过两种方式触发
hash
变化,一种是通过a
标签,并设置href
属性,当用户点击这个标签后,URL
就会发生改变,也就会触发hashchange
事件了:还有一种方式就是直接使用
JavaScript
来对loaction.hash
进行赋值,从而改变URL
,触发hashchange
事件:以下实现我们采用第2种方式来实现。
2.1.2、实现
我们先定义一个父类
BaseRouter
,用于实现Hash
路由和History
路由的一些共有方法;我们简单实现了
push
压入功能、go
前进/后退功能,相关代码的注释都已经标上,简单易懂,就不在一 一介绍,参见如下:2.1.3、效果图
Hash
模式的路由实现例子的效果图如下所示:2.2、History 模式
2.2.1、原理
前面的
hash
虽然也很不错,但使用时都需要加上 #,并不是很美观。因此到了HTML5
,又提供了History API
来实现URL
的变化。其中做最主要的API
有以下两个:history.pushState()
和history.repalceState()
。这两个API
可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:此外,
history
存在下面几个特性:pushState
和repalceState
的标题(title
):一般浏览器会忽略,最好传入null
;popstate
事件来监听url
的变化;history.pushState()
或history.replaceState()
不会触发popstate
事件,这时我们需要手动触发页面渲染;2.2.2、实现
我们同样简单实现了
push
压入功能、go
前进/后退功能,相关代码的注释都已经标上,简单易懂,就不在一 一介绍,参见如下:2.2.3、效果图
History
模式的路由实现例子的效果图如下所示:2.3、两种路由模式的对比
三、总结
本文我们大致介绍了什么路由、前端路由的源起、以及分析了两种前端路由:
Hash
模式和History
模式的原理以及简单功能实现,文中例子的代码实现已经放到 github 上面:https://github.com/fengshi123/router-example 。通过本文对前端路由原理的掌握,这时你就可以基础原理基础去阅读vue-router
和react-router
的源码实现了。github地址为:https://github.com/fengshi123/blog ,上面汇总了作者所有的博客文章,如果喜欢或者有所启发,请帮忙给个 star ~,对作者也是一种鼓励。
参考文献
1、浅谈前端路由:https://www.jianshu.com/p/d2aa8fb951e4
2、前端路由:https://segmentfault.com/a/1190000017853553