您的位置:首页 > Web前端 > Vue.js

前端路由的实现原理(包括Angular、vue等框架)

2018-09-18 21:41 991 查看

这里写自定义目录标题

浏览器路由的介绍

通常来说路由即是URL到函数的映射,比如我们在客户端输入一个URL,该URL会被提交到一个控制器,相应的控制器会对客户端提交上来的路由进行解析, 从而找到处理这个 URL 的类和函数,然后执行。最初web系统的路由都是由服务端来实现的,后来随着前端技术的发展,web系统的路由慢慢都交由前端来实现。

hash路由

我们经常会看到有些URL带有#,这里的#一般分为两种,比如我们可以通过在路由末尾加#来使浏览器页面回到顶部,或者我们在Angular官网(https://angular.cn/tutorial/toh-pt0)上看到的,点击右边的标签来使页面滚动到对应的位置,这是根据#后面的字符串来匹配的,它会使当前页面定位到与之对应id的html标签,若#后面为空即返回顶部,这种我便称之为锚点,还有另外一种路由的方式,我们称之为 hash,hash路由在现在很多流行的主流前端框架使用非常广泛,例如Angular,angular.js,vue等等。
hash路由的实现原理主要是因为路由中hash值的改变,并不会造成浏览器的刷新,并且我们还可以通过hashchange事件来捕获hash值的改变从而来执行对应函数来从服务端获取数据,使页面能够无刷新更新。下面是一段代码示例

window.onhashchange=function(){
var currentHash = location.hash;//当前路由的hash值
var oDiv = $("body");
oDiv.innerHTML='<div>...</div>';//hash路由的改变而改变页面展示
$.ajax({
});//可以通过ajax来获取服务端数据
}

使用hash路由还有一个好处是它能兼容一些低版本的浏览器。
像angular1.x默认使用的就是hash路由,它会对哈希、模版、处理器进行关联,例如下面是angular1.x代码

app.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
$routeProvider
.when('/project', {
templateUrl: '/project.html',
controller: 'ProjectController'
}).otherwise({
redirectTo: '/index'
});
}])

我们可以试试用原生的js来实现类似angular1.x的路由,代码如下:

//路由构造器
function Router() {
//接受所有的配置路由内容:锚 和 函数方法
this.routes = {};            //接受 改变后的 hash值
this.curUrl = '';            //将定义的所有路由和函数方法 传给 routes
this.route = function (path, callback) {
this.routes[path] = callback || function () { };
console.log('routes[path]:' + this.routes[path] )
};            //hash 值改变时触发的 函数方法
this.refresh = function () {
//获取改变的hash值(url中锚 # 号后面的文本)
this.curUrl = location.hash.slice(1) || '/';
this.routes[this.curUrl]();
console.log('location.hash:' + location.hash);
console.log('curUrl:' + this.curUrl);
console.log('this.routes[this.curUrl]:' + this.routes[this.curUrl])
};            //监听load(加载url)、hashchange(hash改变)事件
this.init = function () {
window.addEventListener('load', this.refresh.bind(this), false);
window.addEventListener('hashchange', this.refresh.bind(this), false)
}
}
var R = new Router();//使用Router构造器
R.init();//监听时间
var res = document.getElementById('body');//我们暂时获取body,用以更新body中的页面
//定义所有需要用的 路由:hash值 和 加载的内容
R.route('/', function () {
res.style.background = 'blue';
res.innerHTML = '<div>...</div>';
})
R.route('/index', function () {
res.style.background = 'orange';
res.innerHTML = '<div>...</div>';
})
R.route('/project', function () {
res.style.background = 'black';
res.innerHTML = '<div>...</div>';
})

HTML5 History API

History API是近年最新提出的概念,他包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。
其中history.pushState()和history.replaceState()都能接收三个参数,分别是

1)状态对象(state object) — 它会和同时作为参数的url会关联在一起,我们可以将它理解为一个用来存储自定义数据的元素。无论何时我们导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。

2)标题(title) —一个字符串,目前大多数浏览器都会忽略它(不排除以后会使用,作为页面标题),建议设置为空字符串。

3)地址(URL) — 一条新的历史记录条目的地址(该参数是可选的)。新的URL可能是绝对路径,也可能是相对路径,如果是相对路径,就会自动以当前URL为基准;但是需要注意的是我们传入的URL应与当前URL同源,否则,pushState()会抛出异常。
以上我们可以看出replaceState跟pushState大致相同,唯一的不同之处就是replaceState会替换当前的历史记录,而pushState会在尾部添加一条新的历史记录,这与replace跟push的字面意思相符。
当然我们必须得亲测一下了,首先我打开chrom进入百度然后在控制台输入window.history得到如下信息:

首先我们先测试下pushState,我们继续在控制台输入window.history.pushState(“myState”,null,“https://www.baidu.com/mystate”)
现在浏览器的网址就变成了如下所示,但是页面并没有刷新。

这时候我们再看看window.history的信息:

我们可以发现history中新增了一条记录,从其中的length属性从2变成了3可以看出,而且我们发现其中State属性不在是null,而是我们刚才设置的myState。
接下来我们在测试下replaceState,这次我们输入window.history.replaceState(“replace”,null,“https://www.baidu.com/myreplace”)
这次浏览器网址同样发生了改变如下所示,但是页面同样也没刷新。

同样我们也要看看这时候history中的信息,

我们可以发现这时候history中并没有新增一条记录,length属性仍然为3,但是State属性变成了"repalce",我们还可以试试window.history.back()的方法,我们可以发现回到是https://www.baidu.com/,并没有回到之前的https://www.baidu.com/mystate,这是由于当前的记录已经被替换了。另外还需要值得注意的一点是pushState跟replaceState中的url都不支持跨域,否则会报错。
接下来我们来了解下window.onpopstate事件,从字面意思我们可以发现push是添加,pop是拿出,他们是相对的,自然不难理解这个事件是取出浏览器中的历史记录加载时触发的,但是这个事件要求还是比较严苛的,只有在点击浏览器前进、后退按钮或者是js调用window.history中的back(),go()等方法时才触发,那么假设页面刷新时,这个事件肯定就不会触发了,这个事件被设计出来的初衷应该就是跟pushState和replaceState一起搭配使用的,但是值得注意的是调用pushState和replaceState时也并不会触发window.onpopstate事件哦。接下来我们来看看如何使用这个window.onpopstate事件,先看代码:

// 例如当前的url为:http://test.com/first.html
window.onpopstate=function()
{
// 获得存储在该历史记录中当前的state对象
var myState=window.history.state;
// 我们可以通过不同的myState来加载不同页面
}
//下面是jquery的方法,jQuery对event做了一层包装
$(window).on("popstate", function(event) {
//我们需要通过originalEvent取得原生event。
var state = event.originalEvent.state,
// 本示例直接取URL参数来处理
reg = /page=(\d+)/,
regMatch = reg.exec(location.search),
// 假设第1页的时候是 ?page=1
newPage= regMatch === null ? 1 : +regMatch[1];
updatePage(newPage);//加载当前页面
});

All in all 我们可以发现HTML5 History API使用起来还是非常简单的,它的缺陷可能就在于它的兼容性吧

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: