【造*】是笔者学习和理解一些较复杂的代码结构时的常用方法,它很慢,但是效果却胜过你读十几篇相关的文章。为已知的API方法自行编写实现,遇到自己无法复现的部分再有针对性地去查资料,最后当你再去学习官方代码的时候,就会明白这样做的价值,总有一天,你也将有能力写出大师级的代码。
一. 前端路由
现代前端开发中最流行的页面模型,莫过于SPA
单页应用架构。单页面应用指的是应用只有一个主页面,通过动态替换DOM内容并同步修改url地址,来模拟多页应用的效果,切换页面的功能直接由前台脚本来完成,而不是由后端渲染完毕后前端只负责显示。前端三驾马车Angular
,Vue
,React
均基于此模型来运行的。SPA
能够以模拟多页面应用的效果,归功于其前端路由机制。
前端路由,顾名思义就是一个前端不同页面的状态管理器,可以不向后台发送请求而直接通过前端技术实现多个页面的效果。angularjs中的
ui-router
,vue中的vue-router
,以及react的react-router
均是对这种功能的具体实现。
既然前端路由
这么牛逼,那必须的好好研究一下。
二. 两种实现方式及其原理
常见的路由插件中两种方式都是支持且可以切换的,例如
angularjs1.x
中就可以通过以下代码从Hash模式切换到H5模式:
$locationProvider.html5Mode(true);
切换到HTML5的路由模式,主要用于避免url地址中包含#而引发的问题。
1.HashChange
1.1 原理
HTML
页面中通过锚点定位原理可进行无刷新跳转,触发后url地址中会多出# + 'XXX'
的部分,同时在全局的window
对象上触发hashChange
事件,这样在页面锚点哈希改变为某个预设值的时候,通过代码触发对应的页面DOM改变,就可以实现基本的路由了,基于锚点哈希
的路由比较直观,也是一般前端路由插件中最常用的方式。
1.2 应用
下面通过一个实例看一下,当点击angularjs
的连接时,可以看到控制台打印出了相应的信息。
2.HTML5 HistoryAPI
2.1 原理
HTML5
的History API
为浏览器的全局history
对象增加的扩展方法。一般用来解决ajax请求无法通过回退
按钮回到请求前状态的问题。
在HTML4中,已经支持window.history
对象来控制页面历史记录跳转,常用的方法包括:
- history.forward(); //在历史记录中前进一步
- history.back(); //在历史记录中后退一步
- history.go(n): //在历史记录中跳转n步骤,n=0为刷新本页,n=-1为后退一页。
在HTML5中,window.history
对象得到了扩展,新增的API包括:
- history.pushState(data[,title][,url]);//向历史记录中追加一条记录
- history.replaceState(data[,title][,url]);//替换当前页在历史记录中的信息。
- history.state;//是一个属性,可以得到当前页的state信息。
- window.onpopstate;//是一个事件,在点击浏览器后退按钮或js调用forward()、back()、go()时触发。监听函数中可传入一个event对象,event.state即为通过pushState()或replaceState()方法传入的data参数。
2.2 应用
浏览器访问一个页面时,当前地址的状态信息会被压入历史栈
,当调用history.pushState()
方法向历史栈中压入一个新的state
后,历史栈顶部的指针是指向新的state
的。可以将其作用简单理解为 假装已经修改了url地址并进行了跳转 ,除非用户点击了浏览器的前进
,回退
,或是显式调用HTML4中的操作历史栈的方法,否则不会触发全局的popstate
事件。
在下面的示例中,点击导航按钮,可以看到url
地址栏发生了变化,且控制台打印出了响应的信息。
3.hash 和 history API对比
对比 | hash路由 | History API 路由 |
---|---|---|
url字符串 | 丑 | 正常 |
命名限制 | 通常只能在同一个document 下进行改变 |
url地址可以自己来定义,只要是同一个域名下都可以,*度更大 |
url地址变更 | 会改变 | 可以改变,也可以不改变 |
状态保存 | 无内置方法,需要另行保存页面的状态信息 | 将页面信息压入历史栈时可以附带自定义的信息 |
参数传递能力 | 受到url总长度的限制, | 将页面信息压入历史栈时可以附带自定义的信息 |
实用性 | 可直接使用 | 通常服务端需要修改代码以配合实现 |
兼容性 | IE8以上 | IE10以上 |
三.亲手造一个简单的前端路由插件
造*,不是为了把它装在你的车上,而是当你在荒郊野外开车而*出了问题时多一种选择。
接下来就自己动手实现一个前端路由的插件吧~
3.1基于Hash的前端路由插件myHashRouter.js
我们希望实现的功能是:
- 1.引入
MyHashRouter.js
库 - 2.通过
when()
方法来定义若干不同的路由状态 - 3.通过
init()
方法启动路由功能 - 4.通过点击导航实现前端路由切换
首先编写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地址栏以及内容区域同步更改。
3.2基于History API的前端路由插件myHistoryRouter.js
由于History API
不支持低于IE10以下版本的浏览器(其他大多数现代浏览器基本都支持),所以我们在init()
方法启动时先进行可用性判断,基本代码框架与基于Hash
的路由插件一致。每个方法的实现并不难写,这里不再赘述,笔者自己的代码实现放在附件myHashRouter.js
中,水平有限,仅供参考。
3.3集成说明
为方便理解,本例中将两种模式分开编写,如果是插件库的开发,可以模仿ui-router增加一个html5mode()
的方法,在init()
方法启动路由时,根据所传的参数生成不同的路由插件的单例,也就是我们常说的工厂模式来实现即可。
四.后记
-
造车轮
是一个很好的学习方式,虽然自己造的车轮很简陋,但是对于理解工具的底层原理却很有帮助。 - 本例只是编写了一个路由工具的基本骨架,真正的路由工具还需要做很多功能扩展,个别功能的复杂度也会很高,例如路径的正则匹配,懒加载,组合视图,嵌套视图,路由动画等等,有兴趣的小伙伴可以在本例提供的框架上进行学习扩展。
- 附件说明:
- index_h5history.html —— history API基本用法演示demo
- index_hashchange.html —— hashchange基本用法演示demo
-
router-demo-hash.html —— 引用了
myHashRouter.js
的demo - myHashRouter.js —— 自己开发的基于hash简易路由插件
-
router-demo-hash.html —— 引用了
myHashRouter.js
的demo - myHistoryRouter.js —— 自己开发的基于historyAPI的简易路由插件
-
router-demo-history.html —— 引用了
myHistoryRouter.js
的demo
javascript基础修炼(6)——前端路由的基本原理的更多相关文章
-
javascript基础修炼(10)——VirtualDOM和基本DFS
1. Virtual-DOM是什么 Virtual-DOM,即虚拟DOM树.浏览器在解析文件时,会将html文档转换为document对象,在浏览器环境中运行的脚本文件都可以获取到它,通过操作docu ...
-
javascript基础修炼(4)——UMD规范的代码推演
javascript基础修炼(4)--UMD规范的代码推演 1. UMD规范 地址:https://github.com/umdjs/umd UMD规范,就是所有规范里长得最丑的那个,没有之一!!!它 ...
-
javascript基础修炼(7)——Promise,异步,可靠性
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 别人是开发者,你也是 Promise技术是[javascript异步编程]这个话题中非常重要的,它一度让我感到熟悉 ...
-
javascript基础修炼(8)——指向FP世界的箭头函数
一. 箭头函数 箭头函数是ES6语法中加入的新特性,而它也是许多开发者对ES6仅有的了解,每当面试里被问到关于"ES6里添加了哪些新特性?"这种问题的时候,几乎总是会拿箭头函数来应 ...
-
javascript基础修炼(2)——What's this(上)
目录 一.this是什么 二.近距离看this 三. this的一般指向规则 四. 基本规则示例 五. 后记 开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一.thi ...
-
javascript基础修炼(11)——DOM-DIFF的实现
目录 一. 再谈从Virtual-Dom生成真实DOM 二. DOM-Diff的目的 三. DOM-Diff的基本算法描述 四. DOM-Diff的简单实现 4.1 期望效果 4.2 DOM-Diff ...
-
javascript基础修炼(12)——手把手教你造一个简易的require.js
目录 一. 概述 二. require.js 2.1 基本用法 2.2 细说API设计 三. 造* 3.1 模块加载执行的步骤 3.2 代码框架 3.3 关键函数的代码实现 示例代码托管在我的代码仓 ...
-
javascript基础修炼(9)——MVVM中双向数据绑定的基本原理
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 概述 1.1 MVVM模型 MVVM模型是前端单页面应用中非常重要的模型之一,也是Single Page Appl ...
-
javascript基础修炼(1)——一道十面埋伏的原型链面试题
在基础面前,一切技巧都是浮云. 题目是这样的 要求写出控制台的输出. function Parent() { this.a = 1; this.b = [1, 2, this.a]; this.c = ...
随机推荐
-
iOS保存cookie的方法
SURLRequest*request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com/ ...
-
ORA-12543: TNS:destination host unreachable
在连接Oracle数据库时,如果使用Tnsnames.ora中配置的数据源名称有时会报 ORA-12543: TNS:destination host unreachable 异常,比如:在Tnsna ...
-
修复 XE7 update1 发布 iOS 8.x 实机问题
1. 开启工程目录下面的 Entitlement.TemplateiOS.xml 档案. 2. 加入二行: <key>application-identifier</key> ...
-
PHP非阻塞模式 (转自 尘缘)
让PHP不再阻塞当PHP作为后端处理需要完成一些长时间处理,为了快速响应页面请求,不作结果返回判断的情况下,可以有如下措施: 一.若你使用的是FastCGI模式,使用fastcgi_finish_re ...
-
IO流 总结一
字符流: FileReader FileWriter BufferedReader BufferedWriter readLine(); 字节流: FileInputReader FileOutput ...
-
wpf的datagrid和winform的datagridview刷新
DataGrid的数据源的加载需要大量IO操作,不可能等数据全部读取之后才显示到UI上.由于对WPF数据绑定不很熟悉,对ObserveCollection等内容没有太多时间去研究,只能用一些取巧的办法 ...
- 华为交换机VRP系统命令试图
-
[OpenCV-Python] OpenCV 中的 Gui特性 部分 II
部分 IIOpenCV 中的 Gui 特性 OpenCV-Python 中文教程(搬运)目录 4 图片 目标 • 在这里你将学会怎样读入一幅图像,怎样显示一幅图像,以及如何保存一幅图像 • 你将要学习 ...
-
mac下VirtualBox跟linux虚拟机共享文件夹
1.在VirtualBox中设置好共享目录,设置自动挂载/固定分配 2.安装增强工具,为了避免安装出错需要安装依赖文件 #更新内核. yum update kernel#需要安装相应的kernel-d ...
-
Oracle_PL/SQL(5) 包
包1.定义:包用于逻辑组合相关的PL/SQL类型,项和子程序,由包规范和包体组成 建立包规范:包规范是包与应用程序之间的接口,用于定义包的公用组件, 包括常量,变量,游标,过程,函数等 建立包体:用于 ...