前端路由原理
angular,vue,react都用过的吧,即使没用过也听过的吧,这些框架中都用到了路由这玩意,那么就来了解下他的原理吧。 前端路由的话,分为两类,一个是hash路由,另一个是HTML5的history路由。
一、hash路由
hash路由是我们经常用到的一种路由方式,因为他的兼容性相当的好,可以兼容到ie8。
hash路由的原理是监听url中#后部的变化,配合锚点的点击,触发hashchange事件,根据window.location.hash来做相应的处理。详细的可以查看:MDN。
直接上代码,看下Hash原型,
var Hash = function() {
this.hashFn = {}; // 存放路由与事件
window.onhashchange = (e) => {
var newUrl = e.newURL,
newHash = newUrl.match(/#(w*)$/),
env = this.hashFn[ newHash[1] ];
env.fn.apply(env.ctx);
};
}
Hash.prototype.bind = function(hash, fn, ctx = window) {
this.hashFn[ hash ] = {
fn,
ctx
}
}
hashFn用来存储对应hash值与其处理函数,随后通过监听hashchange事件,根据即将进入的hash值触发对应的处理函数。bind函数用于向hashFn中添加以hash为key值的处理函数和他的处理上下文,以供触发时使用。
var router = new Hash();
router.bind('a', function() {
console.log('aaa');
});
router.bind('b', function() {
console.log('bbb');
});
router.bind('c', function() {
console.log('ccc');
});
利用new创建实例,并绑定好数据,此时点击页面上的链接就有反应了。demo可以查看:前端路由原理-hash。
二、h5 history
使用h5 history api创建路由时,相比hash没有了#号,美观了很多。但是history的兼容性并不是太好,需要ie10+以上才支持。
h5 histroy api常用的有以下几个:
- history.go(n),n为正数表示向前移动n个页面,负数亦然,n为0或为空时表示刷新当前页面。
- history.forward(),相当于go(1)。
- history.back(),相当于go(-1)。
h5中新增的:
- history.pushState(data, title [, url]),向历史纪录中添加一条记录。
- history.replaceState(data, title [, url]),更改当前的路由历史记录。
- popstate事件,当历史记录发生改变时,就会触发该事件,即浏览器上的前进后退按钮事件和go()、forward()、back()事件,pushState和replaceState并不会触发该事件。
定义一个对象负责绑定事件和监听并触发事件,
var His = function() {
this.eventFn = {};
window.onpopstate = (e) => {
this.eventTrigger(e.state)
};
}
His.prototype.eventTrigger = function(state) {
var name = state.name,
env = this.eventFn[ name ];
env.fn.apply(env.ctx);
};
His.prototype.bind = function(name, fn, ctx = window) {
this.eventFn[ name ] = {
fn,
ctx
}
};
bind方法用于绑定要监听的事件和处理函数,eventTrigger用于触发bind绑定的函数。当浏览器获取到popstate事件时,就会根据触发对应的事件。 创建个实例,绑定下事件,
var his = new His();
his.bind('a', function() {
console.log('a');
});
his.bind('b', function() {
console.log('b');
});
his.bind('c', function() {
console.log('c');
});
增加html,增加触发的条件,
<button onclick="a()">a</button>
<button onclick="b()">b</button>
<button onclick="c()">b</button>
<p id="box">^!^</p>
function a() {
history.pushState({
name: 'a'
}, 'a', location.href.split('?')[0] + '?name=a');
his.eventTrigger({name: 'a'});
}
function b() {
history.pushState({
name: 'b'
}, 'b', location.href.split('?')[0] + '?name=b');
his.eventTrigger({name: 'b'});
}
function c() {
history.pushState({
name: 'c'
}, 'c', location.href.split('?')[0] + '?name=c');
his.eventTrigger({name: 'c'});
}
为啥要在最后加个his.eventTrigger()函数呢?因为前面也说了,pushState并不会触发popstate事件。
这样history路由就已经完成了。demo:前端路由原理-history。