前端路由的实现方式

在讲前端路由之前,先说下后端路由,以及为什么出现了前端路由。 
后端路由:  浏览器在地址栏中切换不同的url时,每次都向后台服务器发出请求,服务器响应请求,在后台拼接html文件传给前端显示,java web中的jsp就是如此实现的。常用的后台MVC模式的基本路由处理流程:浏览器输入一个url请求,从中找到Controller和Action的值,将请求传递给Controller处理,Controller获取Model数据对象,并且将Model传递给View,最后View呈现界面。 
例如输入一个url:localhost/home/index 
其中localhost是域名,对应结构{controller}/{action}/{id}
  • 优点:分担了前端的压力,html和数据的拼接都是由服务器完成。
  • 缺点:当项目十分庞大时,加大了服务器端的压力,同时在浏览器端不能输入制定的url路径进行指定模块的访问。另外一个就是如果当前网速过慢,那将会延迟页面的加载,对用户体验不是很友好。
前端路由:  随着(SPA)单页应用的不断普及,前后端开发分离,目前项目基本都使用前端路由,在项目使用期间页面不会重新加载。
  • 优点:1、用户体验好,和后台网速没有关系,不需要每次都从服务器全部获取,界面展现快。2、可以再浏览器中输入指定想要访问的url路径地址。3.实现了前后端的分离,方便开发。有很多框架都带有路由功能模块。
  • 缺点:1、对SEO不是很友好2、在浏览器前进和后退时候重新发送请求,没有合理缓存数据。3,初始加载时候由于加载所有模块渲染,会慢一点。

前端路由目前主要有两种方法:
  • 1、利用url的hash,就是常用的锚点(#)操作,类似页面中点击某小图标,返回页面顶部,JS通过hashChange事件来监听url的改变,IE7及以下需要轮询进行实现。一般常用框架的路由机制都是用的这种方法,例如Angualrjs自带的ngRoute和二次开发模块ui-router,react的react-route,vue-route…
  • 2、利用HTML5的History模式,使url看起来类似普通网站,以”/”分割,没有”#”,但页面并没有跳转,不过使用这种模式需要服务器端的支持,服务器在接收到所有的请求后,都指向同一个html文件,通过historyAPI,监听popState事件,用pushState和replaceState来实现。
1.利用hash
我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash,大型框架的路由系统大多都是哈希实现的。
同样我们需要一个根据监听哈希变化触发的事件 —— hashchange 事件
我们用  window.location  处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。

以 hash 形式为例,当 url 的 hash 发生变化时,触发 hashchange 注册的回调,回调中去进行不同的操作,进行不同的内容的展示。
<body>
<ul>
<li><a href="#/shouye">首页</a></li>
<li><a href="#/home">主页</a></li>
<li><a href="#/about">详情页</a></li>
</ul>
<script type="text/javascript" src="router.js"></script>
<script type="text/javascript">
var content = document.querySelector('body');
//给window对象挂载属性
window.Router = new Router();
window.Router.init();
Router.route('/shouye', function() {
alert("这是shou页");
});
Router.route('/home', function() {
alert("这是主页");
});
Router.route('/about', function() {
alert("这是详情页");
});
</script>
</body>
router.js
//构造函数
function Router() {
this.routes = {};
this.currentUrl = '';
}
//route 存储路由更新时的回调到回调数组routes中,回调函数将负责对页面的更新
Router.prototype.route = function(path, callback) {
this.routes[path] = callback || function(){};//给不同的hash设置不同的回调函数
};
//refresh 执行当前url对应的回调函数,更新页面
Router.prototype.refresh = function() {
console.log(location.hash.slice(1));//获取到相应的hash值
this.currentUrl = location.hash.slice(1) || '/';//如果存在hash值则获取到,否则设置hash值为/
// console.log(this.currentUrl);
this.routes[this.currentUrl]();//根据当前的hash值来调用相对应的回调函数
};
//init 监听浏览器url hash更新事件
Router.prototype.init = function() {
window.addEventListener('load', this.refresh.bind(this), false);
window.addEventListener('hashchange', this.refresh.bind(this), false);
}

②模拟hashchange 在低版本 IE 需要通过轮询监听 url 变化来实现:

(function(window) {
// 如果浏览器不支持原生实现的事件,则开始模拟,否则退出。
if ( "onhashchange" in window.document.body ) { return; }
var location = window.location,
oldURL = location.href,
oldHash = location.hash;
// 每隔100ms检查hash是否发生变化
setInterval(function() {
var newURL = location.href,
newHash = location.hash;
// hash发生变化且全局注册有onhashchange方法(这个名字是为了和模拟的事件名保持统一);
if ( newHash != oldHash && typeof window.onhashchange === "function" ) {
// 执行方法
window.onhashchange({
type: "hashchange",
oldURL: oldURL,
newURL: newURL
});
oldURL = newURL;
oldHash = newHash;
}
}, 100);
})(window);
2.利用 History API

history对象保存着用户上网的历史记录,从窗口被打开的那一刻算起。由于安全方面的考虑,开发人员无法得到用户浏览器的URL,但借由用户访问过的页面列表,可以在不知道实际URL的情况下实现后退和前进。
length
history.length属性保存着历史记录的URL数量。初始时,该值为1。如果当前窗口先后访问了三个网址,history.length属性等于3
跳转方法
history对象提供了一系列方法,允许在浏览历史之间移动,包括go()、back()和forward()

【go()】
使用go()方法可以在用户的历史记录中任意跳转。这个方法接收一个参数,表示向后或向前跳转的页面数的一个整数值。负数表示向后跳转(类似于后退按钮),正数表示向前跳转(类似于前进按钮)
//后退一页
history.go(-1)
//前进一页
history.go(1);
//前进两页
go()方法无参数时,相当于history.go(0),可以刷新当前页面
//刷新当前页面
history.go();
//刷新当前页面
history.go(0);
【back()】
back()方法用于模仿浏览器的后退按钮,相当于history.go(-1)

【forward()】
forward()方法用于模仿浏览器的前进按钮,相当于history.go(1)
//后退一页
history.back()
//前进一页
history.forward()

【state】
history.state属性返回当前页面的state对象
history.pushState({page: 1}, 'title 1', '?page=1');
history.state// { page: 1 }
【popstate事件】
每当同一个文档的浏览历史(即history对象)出现变化时,就会触发popstate事件
需要注意的是,仅仅调用pushState方法或replaceState方法,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用javascript调用back()、forward()、go()方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发

使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)
window.onpopstate = function (event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
};  

上面代码中的event.state,就是通过pushState和replaceState方法,为当前URL绑定的state对象

这个state对象也可以直接通过history对象读取

var currentState = history.state;

如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是静默失败
[注意] 使用历史记录时,页面通常从浏览器缓存之中加载,而不是重新要求服务器发送新的网页

重点说其中的两个新增的API  history.pushState  和  history.replaceState
这两个 API 都接收三个参数,分别是
  • 状态对象(state object) — 一个JavaScript对象,与用pushState()方法创建的新历史记录条目关联。无论何时用户导航到新创建的状态,popstate事件都会被触发,并且事件对象的state属性都包含历史记录条目的状态对象的拷贝。
  • 标题(title) — FireFox浏览器目前会忽略该参数,虽然以后可能会用上。考虑到未来可能会对该方法进行修改,传一个空字符串会比较安全。或者,你也可以传入一个简短的标题,标明将要进入的状态。
  • 地址(URL) — 新的历史记录条目的地址。浏览器不会在调用pushState()方法后加载该地址,但之后,可能会试图加载,例如用户重启浏览器。新的URL不一定是绝对路径;如果是相对路径,它将以当前URL为基准;传入的URL与当前URL应该是同源的,否则,pushState()会抛出异常。该参数是可选的;不指定的话则为文档当前URL。
相同之处 是两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。
不同之处 在于,pushState会增加一条新的历史记录,而replaceState则会替换当前的历史记录。
[注意]: 这里的 url 不支持跨域

<body>
<p>You are at coordinate <span id="coord">5</span> on the line.</p>
<p>
<a href="?x=6" οnclick="go(1); return false;">Advance to 6</a> or
<a href="?x=4" οnclick="go(-1); return false;">retreat to 4</a>?
</p>
<script>
var currentPage = 5; // prefilled by server!!!!
function go(d) {
setupPage(currentPage + d);
history.pushState(currentPage, document.title, '?x=' + currentPage);
}
onpopstate = function(event) {
setupPage(event.state); //指向pushState和replaceState方法为当前URL所提供的状态对象(即这两个方法的第一个参数)
}
function setupPage(page) {
currentPage = page;
document.title = 'Line Game - ' + currentPage;
document.getElementById('coord').textContent = currentPage;
document.links[0].href = '?x=' + (currentPage+1);
document.links[0].textContent = 'Advance to ' + (currentPage+1);
document.links[1].href = '?x=' + (currentPage-1);
document.links[1].textContent = 'retreat to ' + (currentPage-1);
}
</script>
</body>

部分代码参考自:https://segmentfault.com/a/1190000007238999

(前端下白,如有错误,欢迎指正~~)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值