【造轮子】是笔者学习和理解一些较复杂的代码结构时的经常使用方法,它很慢,可是效果却赛过你读十几篇相关的文章。为已知的API方法自行编写实现,遇到本身没法复现的部分再有针对性地去查资料,最后当你再去学习官方代码的时候,就会明白这样作的价值,总有一天,你也将有能力写出大师级的代码。html
现代前端开发中最流行的页面模型,莫过于SPA
单页应用架构。单页面应用指的是应用只有一个主页面,经过动态替换DOM内容并同步修改url地址,来模拟多页应用的效果,切换页面的功能直接由前台脚原本完成,而不是由后端渲染完毕后前端只负责显示。前端三驾马车Angular
,Vue
,React
均基于此模型来运行的。SPA
可以以模拟多页面应用的效果,归功于其前端路由机制。前端
前端路由,顾名思义就是一个前端不一样页面的状态管理器,能够不向后台发送请求而直接经过前端技术实现多个页面的效果。angularjs中的
ui-router
,vue中的vue-router
,以及react的react-router
均是对这种功能的具体实现。vue
既然前端路由
这么牛逼,那必须的好好研究一下。html5
常见的路由插件中两种方式都是支持且能够切换的,例如
angularjs1.x
中就能够经过如下代码从Hash模式切换到H5模式:
$locationProvider.html5Mode(true);
切换到HTML5的路由模式,主要用于避免url地址中包含#而引起的问题。react
HTML
页面中经过锚点定位原理可进行无刷新跳转,触发后url地址中会多出# + 'XXX'
的部分,同时在全局的window
对象上触发hashChange
事件,这样在页面锚点哈希改变为某个预设值的时候,经过代码触发对应的页面DOM改变,就能够实现基本的路由了,基于锚点哈希
的路由比较直观,也是通常前端路由插件中最经常使用的方式。angularjs
下面经过一个实例看一下,当点击angularjs
的链接时,能够看到控制台打印出了相应的信息。
ajax
HTML5
的History API
为浏览器的全局history
对象增长的扩展方法。通常用来解决ajax请求没法经过回退
按钮回到请求前状态的问题。vue-router
在HTML4中,已经支持window.history
对象来控制页面历史记录跳转,经常使用的方法包括:后端
在HTML5中,window.history
对象获得了扩展,新增的API包括:api
浏览器访问一个页面时,当前地址的状态信息会被压入历史栈
,当调用history.pushState()
方法向历史栈中压入一个新的state
后,历史栈顶部的指针是指向新的state
的。能够将其做用简单理解为 伪装已经修改了url地址并进行了跳转 ,除非用户点击了浏览器的前进
,回退
,或是显式调用HTML4中的操做历史栈的方法,不然不会触发全局的popstate
事件。
在下面的示例中,点击导航按钮,能够看到url
地址栏发生了变化,且控制台打印出了响应的信息。
对比 | hash路由 | History API 路由 |
---|---|---|
url字符串 | 丑 | 正常 |
命名限制 | 一般只能在同一个document 下进行改变 |
url地址能够本身来定义,只要是同一个域名下均可以,自由度更大 |
url地址变动 | 会改变 | 能够改变,也能够不改变 |
状态保存 | 无内置方法,须要另行保存页面的状态信息 | 将页面信息压入历史栈时能够附带自定义的信息 |
参数传递能力 | 受到url总长度的限制, | 将页面信息压入历史栈时能够附带自定义的信息 |
实用性 | 可直接使用 | 一般服务端须要修改代码以配合实现 |
兼容性 | IE8以上 | IE10以上 |
造轮子,不是为了把它装在你的车上,而是当你在荒郊野外开车而轮子出了问题时多一种选择。
接下来就本身动手实现一个前端路由的插件吧~
myHashRouter.js
咱们但愿实现的功能是:
MyHashRouter.js
库when()
方法来定义若干不一样的路由状态init()
方法启动路由功能首先编写js骨架,如图所示:
;(function() { function Router() { //记录路由的跳转历史 this.historyStack = []; //记录已注册的路由信息 this.registeredRouter = []; //路由匹配失败时跳转项 this.otherwiseRouter = { path: '/', content: 'home page' } } /* * 启动路由功能 */ Router.prototype.init = function() { } /* * 绑定window.onhashchange事件的回调函数 */ Router.prototype._bindEvents = function() { } /** * 路由注册方法 */ Router.prototype.when = function(path, content) { } /** * 判断新添加的路由是否已存在 */ Router.prototype._hasThisRouter = function(path) { } /** * 路由不存在时的指定地址 */ Router.prototype.otherwise = function(path, content) { } /** * 路由跳转方法,主动调用时可用于跳转路由 */ Router.prototype.go = function(topath) { } /** * 用于将对应路由信息渲染至页面,实现路由切换 */ Router.prototype.render = function (content) { } var router = new Router(); //将接口暴露至全局 window.$router = router; })();
完成了路由插件的编写后,咱们在demo中引入该库,而后使用when()
方法注册几个路由地址,再使用init()
方法启动路由,脚本部分代码以下:
效果:
运行附件中的router-demo-hash.html
,点击导航按钮,便可看到url地址栏以及内容区域同步更改。
myHistoryRouter.js
因为History API
不支持低于IE10如下版本的浏览器(其余大多数现代浏览器基本都支持),因此咱们在init()
方法启动时先进行可用性判断,基本代码框架与基于Hash
的路由插件一致。每一个方法的实现并不难写,这里再也不赘述,笔者本身的代码实现放在附件myHashRouter.js
中,水平有限,仅供参考。
为方便理解,本例中将两种模式分开编写,若是是插件库的开发,能够模仿ui-router增长一个html5mode()
的方法,在init()
方法启动路由时,根据所传的参数生成不一样的路由插件的单例,也就是咱们常说的工厂模式来实现便可。
造车轮
是一个很好的学习方式,虽然本身造的车轮很简陋,可是对于理解工具的底层原理却颇有帮助。myHashRouter.js
的demomyHashRouter.js
的demomyHistoryRouter.js
的demo