# 前端路由
## Hash
**通过使用hash+hashChange实现**
我们知道,使用location.hash方法返回的是url中#后面的部分,比如说:
~~~
// https://www.baidu.com/#hello在窗口中输入
location.hash
// 返回#hello
~~~
这样我们知道,只要触发了hashchange方法,我们就可以监听一些变化。可以使用a标签或者直接使用js对location.hash进行操作来触发change方法。
在下面是一个最简单的Router实现。
~~~
class Routers {
constructor() {
this.routes = {};
this.currentUrl = '';
this.refresh = this.refresh.bind(this);
window.addEventListener('load', this.refresh, false);
window.addEventListener('hashchange', this.refresh, false);
}
route(path, callback) {
this.routes[path] = callback || function () { };
}
refresh() {
this.currentUrl = location.hash.slice(1) || '/';
this.routes[this.currentUrl]();
console.log(this.routes);
}
}
~~~
```html
<body>
<ul>
<li>
<a href="#/">turn yellow</a>
</li>
<li>
<a href="#/blue">turn blue</a>
</li>
<li>
<a href="#/green">turn green</a>
</li>
</ul>
</body>
```
在这个html中,我们实例化Router,就可以得到一个存有不同路由对应回调的routers对象。如下:
~~~
window.Router = new Routers();
var content = document.querySelector('body');
// change Page anything
function changeBgColor(color) {
content.style.backgroundColor = color;
}
Router.route('/', function () {
changeBgColor('yellow');
});
Router.route('/blue', function () {
changeBgColor('blue');
});
Router.route('/green', function () {
changeBgColor('green');
});
~~~
这样我们点击不同的a标签时就可以触发不同路由的回调,对dom进行改变。
带有回退功能的router:
~~~
class Routers {
constructor() {
this.routes = {};
this.currentUrl = '';
this.history = [];
this.currentIndex = this.history.length - 1;
this.refresh = this.refresh.bind(this);
this.isBack = false;
window.addEventListener('load', this.refresh, false);
window.addEventListener('hashchange', this.refresh, false);
}
route(path, callback) {
this.routes[path] = callback || function () { };
}
refresh() {
this.currentUrl = location.hash.slice(1) || '/';
if (!this.isBack) {
if (this.currentIndex < this.history.length - 1)
this.history = this.history.slice(0, this.currentIndex + 1);
this.history.push(this.currentUrl);
this.currentIndex++;
}
this.routes[this.currentUrl]();
console.log(this.history);
this.isBack = false;
}
backOff() {
this.isBack = true;
this.currentIndex <= 0
? (this.currentIndex = 0)
: (this.currentIndex = this.currentIndex - 1);
location.hash = `#${this.history[this.currentIndex]}`
this.routes[this.history[this.currentIndex]]();
}
}
~~~
## Hisotry
第一个实现形式是靠History API实现的。主要方法有history.pushState 和 history.replaceState。
#### history.pushState
history.pushState可以传入三个参数:状态对象 标题 与URL。
状态对象:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。
**URL参数实现无刷新改变URL**
我们可以在浏览器中做这个实验。输入:
~~~
window.history.pushState(null,null,'https://www.baidu.com/?name=dodf')
~~~
可以发现浏览器的url变成了https://www.baidu.com/?name=dodf。 页面并没有刷新,但是url存到了浏览器历史中。通过这个第三个参数,我们可以实现浏览器无刷新的情况下改变url。
#### history.replaceState
替换history,也是不发生跳转。
#### history.popState
在history发生变化时触发。
> 仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用back、forward、go方法时才会触发。
>
简单实现如下:
~~~
class Routers {
constructor() {
this.routes = {};
this._bindPopState();
}
init(path) {
history.replaceState({ path: path }, null, path);
this.routes[path] && this.routes[path]();
}
route(path, callback) {
this.routes[path] = callback || function () { };
}
go(path) {
history.pushState({ path: path }, null, path);
this.routes[path] && this.routes[path]();
}
_bindPopState() {
window.addEventListener('popstate', e => {
const path = e.state && e.state.path;
this.routes[path] && this.routes[path]();
});
}
}
~~~
## 参考
[面试官: 你了解前端路由吗?](https://juejin.im/post/5ac61da66fb9a028c71eae1b#comment)
[前端路由](http://blog.xiaoboma.com/2017/01/24/%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1/)
- html
- 冒泡/捕获/委托
- 前端路由
- Dom
- 创建节点API
- 页面修改型API
- 节点查询型API
- 节点关系型API
- 元素属性API
- DOM事件
- classList
- 性能优化
- 节流防抖
- localStorage sessionStorage
- BOM
- meta
- data属性
- js实现拖拽
- html5
- 关于meta
- 轮播图
- js实现拖放
- 电话号inputFormater
- js
- es6
- promise
- iterator
- generator
- async
- proxy
- Set
- Map
- Object的扩展
- String
- Iterator
- Symbol
- 解构赋值
- 函数式编程
- module
- 基本数据类型
- 数组相关codings
- for of/for in
- this
- call bind apply
- 闭包
- 作用域
- prototype与继承
- 深浅拷贝
- setTimeOut/setInterval
- 垃圾处理机制
- 设计模式
- 观察者模式
- 单例模式
- 策略模式
- RegExp
- with
- 其他玩意
- Error/Stack Trace
- 面向对象
- css
- 回流重绘
- %取值
- 属性继承/属性优先级
- flex
- BFC
- 盒模型
- 设置css的方法
- 定位机制
- 块级/行内元素
- hack和一些别的玩意
- css动画
- 几个布局
- 画图形
- css3
- animation对比transform
- 点击不同图片区域跳转不同
- css选择器性能问题
- vh rem em
- css选择器
- 伪类伪元素
- css匹配原理
- 数据结构与算法
- 数据结构
- 树
- 链表
- 栈和队列
- 排序
- 归并排序
- 插入排序
- 选择排序
- 冒泡排序
- 快速排序
- 递归
- 回溯法
- 搜索算法
- 动态规划
- http
- 跨域问题
- CORS
- GET/POST
- ajax
- ajax上传文件
- http缓存
- https
- TCP/UDP
- cookie/session
- http2.0
- spdy
- websocket
- React
- redux
- 生命周期
- 虚拟dom与diff
- 双向数据绑定
- mvvm
- setState
- contextApi props reudx
- 高阶组件
- react-redux
- Fiber
- react-router
- 受控/非受控组件
- 待整理
- webpack
- loader实现
- 前端安全
- 移动端适配
- Vue
- 传值
- 其他