一、什么是前端路由
在web开发的过程中,路由的使用是必不可少的,这里的路由不是指我们日常生活中的路由器,但是实现原理基本相同,它代表一个url与相应处理程序的影射关系,用户在输入要访问的url之后,路由会解析url中的路径,之后根据映射表中的映射关系查找相应的预设函数,并将结果返回给用户,以此完成一次操作。
前端路由它是通过一个hash函数或者H5提供的history API来实现。在进行开发时,路由用来设定访问路径,并将路径与相应的组件映射起来,用户在访问相应的路径时,路由根据映射关系实现不同组件间的切换,整个过程都是在同一个页面中实现,不涉及页面间的跳转,也就是我们常说的SPA(single page application)单页应用。
二、前端路由的优势
- 页面刷新速度快。由于后端路由在请求一个新路径时,会重新向服务器发送请求,之后再根据服务器的相应结果重新渲染页面,这个过程会受到网络延迟的影响,而前端路由省略了整个请求过程,只是完成部分组件间的切换,因此页面的刷新速度会相对较快,用户体验相对较好。
- 页面状态可记录。在某些场合中,用ajax请求,可以让页面无刷新,页面变了但Url没有变化,用户就不能复制到想要的地址,用前端路由做单页面网页就很好的解决了这个问题
三、前端路由的实现方法
目前前端路由的实现方法主要有两种方法,location.hash和window.history。
1.location.hash实现前端路由
hash就是一个url中#后面的部分,也叫做url的锚部分,这个 # 有两种情况,第一种是我们所谓的锚点,比如页面目录跳转。第二种就是路由里面的#,我们称之为hash,通过hash方法实现前端路由主要是用到的是onhashchange事件,这个事件可以实时监听url中hash值的变化,由此来根据hash值的变化进行一些DOM的切换操作。
-
onhashchange
事件
window.onhashchange = func;
<body onhashchange="func();">
window.addEventListener("hashchange", func, false);
了解了hash的基本原理后,我们再来看下如何用它实现路由:
设计思路:
当浏览器地址栏url的hash值发生变化时,就会触发onhashchange事件,这时通过window.location.hash可以拿到当前浏览器的url的hash值,注意此时的hash值是带有#的,因此需要对其值进行相应的处理,去掉#,并且如果当前url不含hash值的话,就将其当做根目录处理。之后将url和相应的组件函数进行映射,根据不同的hash值执行不同的回调函数,也就是加载相应的组件。
代码如下:
(function () {
function Router() {
this.routes = {};//用来保存路由
this.curentUrl = ''; //获取当前的hash
}
// 在Router.prototype原型上设置方法,第一个设置路由,第二个路由跳转,第三个页面初始化
Router.prototype.route = function (path, callback) {
//保存路由对应的函数,创建url和组件的映射关系
this.routes[path] = callback;
// console.log( this.routes );
}
Router.prototype.reloadPage = function () {
this.curentUrl = location.hash.substring(1) || '/index'; //获取hash值,并截取#后的内容
this.routes[this.curentUrl](); //根据路由名 执行对应的函数
}
Router.prototype.init = function () { //监听路由变化
// 当页面完全加载后触发事件
window.addEventListener('load', this.reloadPage.bind(this));
// hash变化触发事件
window.addEventListener('hashchange', this.reloadPage.bind(this));
}
window.Router = Router;
})();
// 指定容器渲染组件
function display_page(url) {
$("#router").load(url)
}
// 创建实例并初始化
var oRouter = new Router();
oRouter.init();
// 创建映射关系
oRouter.route('/index', function () {
display_page('./page/main.html');
});
oRouter.route('/news', function () {
display_page('./page/news.html');
});
oRouter.route('/about', function () {
display_page('./page/about.html');
});
2.window.history和popstate实现前端路由
相关API:
window.history.pushState(state, title, url)
// state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
// title:标题,基本没用,一般传 null
// url:设定新的历史记录的 url。新的 url 与当前 url 的 origin 必须是一樣的,否则会抛出错误。url可以是绝对路径,也可以是相对路径。
//如 当前url是 https://www.baidu.com/a/,执行history.pushState(null, null, './qq/'),则变成 https://www.baidu.com/a/qq/,
//执行history.pushState(null, null, '/qq/'),则变成 https://www.baidu.com/qq/
window.history.replaceState(state, title, url)
// 与 pushState 基本相同,但她是修改当前历史记录,而 pushState 是创建新的历史记录
window.addEventListener("popstate", function() {
// 监听浏览器前进后退事件,pushState 与 replaceState 方法不会触发
});
window.history.back() // 后退
window.history.forward() // 前进
window.history.go(1) // 前进一步,-2为后退两步,window.history.lengthk可以查看当前历史堆栈中页面的数量
设计思路:
当想要跳转到指定url的时候,将目标url通过pushState()或者replaceState()方法填入到history和地址栏中,此时由于上述两种方法不会主动进行页面刷新,因此页面仍停留在当前页面,只是url地址发生了改变。之后通过popstate事件响应,执行相应的回调函数,实现前端组件间的切换。
代码如下:
(function () {
var url = 'nav1';
history.replaceState(url,null,'');//最开始的状态,采用replace直接替换
$('#router').html('<p>'+url+'</p>');
$('a').on('click', function () {
console.log(this.text)
url = this.text;
$('#router').html('<p>' + url + '</p>')
history.pushState(url, null, '#/' + url);
})
window.addEventListener('popstate', function (e) {
console.log(e.state);
url = e.state
$('#router').html('<p>' + url + '</p>')
});
window.addEventListener('load', function () {
url = location.hash.slice(2) || 'nav1';
history.replaceState(url, null, '');
console.log(location.hash);
$('#router').html('<p>' + url + '</p>');
});
})()