1、单页面应用(SPA)的概念:
1、single-page application是一种特殊的Web应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript、CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转,而是利用JavaScript动态的变换HTML(采用的是div切换显示和隐藏),从而实现UI与用户的交互。
2、简单来说SPA的网页只有一个页面,而这个网页的实际方式要能够回应使用者所使用的各种装置并且赋值使用者在电脑上使用软件的体验,让使用者可以更容易和有效的使用网站。按照正常情况下,我们会在一个页面中链接到其他的很多个页面,进行页面的跳转,但是如果使用单页面应用的话,我们始终在一个页面中,通常使用a标签的描点来实现。
2、作用(好处)
1、由于避免了页面的重新加载,SPA可以提供较为流畅的用户体验。得益于Ajax,可以实现无跳转刷新,由于与浏览器的history机制,可以使用hash的b变化从而可以实现推动界面变化。
2、只要使用支持HTML5和CSS3的浏览器就可以执行复杂的SPA,因此,开发人员不必为了写SPA网站而特别学习另一个开发方式,而使用者也不额外安装软件,所以,让开发SPA网页程序的入门和使用门槛降低不少。
3、缺点
以SPA方式开发的网站不容易管理也不够安全。
因为没了一页一页的网页给搜索引擎的爬虫来爬,所以,在搜索引擎最佳化(SEO)的工作上,需要花费额外的功夫。
因为没有换页,需要自定义状态来取代传统网页程序以网址来做判断。
4、实现SPA
技术:
1、处理#后面的字符
2、局部刷新3、iframe
1、#后面的字符
后面的字符,其实是location对象的hash属性的值,即是说,我们可以轻松拿到这个#后面字符的变化值,代码如下:
var hash = location.hash;
既然值能拿到,就可以直接通过一个a标签跳转。
<a href="#luoxuan">罗旋</a>;
<a href="#xiexing">谢星</a>;
<a href="#luoli">罗粒</a>;
<a href="#luobo">罗卜</a>;
2、局部刷新(Ajax)
ajax+div+historyapi
这种方式实现要更复杂,开发要自己实现url管理,以达到前进、后退跳转等能力,不过目前都已经有成熟的路由库可以使用,另外基于div模式的SPA,开发需要考虑全局对局部的影响,包括css、事件等。这种方式的优点是刷新要更轻量,js库和css样式在首次加载即可,局部页面可以只加载少量的数据,并且基于div响应式效果在移动端要更好。因此这也成了目前流行的前端框架angular、react等选用的方案。
代码实现:
<script>
window.addEventListener('haschange',function(){
var hash = document.location.hash;
switch(hash){
case '#luoxuan':
$.ajax({
url:'./json/luoxaun.json',
success:function(){
document.write("罗旋是小仙女!!!")
}
});
break;
case '#xiexing':
$.ajax({
url:'./json/xiexing.json',
success:function(){
document.write("谢星是个帅哥哥哦~~~")
}
});
break;
}
})
</script>
3、iframe
其一,使用iframe的优点之一就是开发简单,目前的浏览器都已经对iframe url发生修改产生历史记录。
其二,除了响应式问题的兼容性不好之外(也正因此iframe很不适合用在移动端),iframe作为使用多年的浏览器技术之一,在许多方面的兼容性要好许多,也是一些新技术在低版本浏览器上不可用时的替代解决方案,如contentEditable。
其三,iframe与父文档相对独立,可以不受父文档的影响,想必这也是目前一些网站(网易云音乐,QQ空间,各大邮箱)继续使用iframe的主要原因。
基于iframe制作单页博客
笔者的博客制作于2015年,当时的PC浏览器应该不支持iframe历史记录,所以笔者选择通过修改hash的方式实现历史记录(浏览器对hash的修改会记录历史记录),选择基于iframe制作基于两个原因:一、希望浏览博客时不论怎么跳转,可以不中断播放音乐;二、iframe相对全站ajax+div而言要更简单易行。博客地址http://movesun.com,博客布局参考 http://www.kotonohanoniwa.jp/。
做法是绑定所有需要在iframe中打开的a标签的click事件,当点击a标签时,将a标签url中的path路径修改为浏览器url的hash值。例如我想访问的是 http://movesun.com/blog/list,则将/blog/list作为hash值设置到地址栏 ,因此在浏览器地址栏看到的地址就变为了http://movesun.com/#/blog/list,
因此在父文档中有这样一段js
1 $('a[target="contentFrame"]').click(function(){ 2 var $this = $(this), 3 url = $this.attr('href'), 4 mainHost = location.host, 5 i = url.indexOf(mainHost); 6 $active.removeClass('active'); 7 $active = $this.parent('li'); 8 $active.addClass('active'); 9 if(i !== -1){ 10 url = url.substr(i + mainHost.length); 11 } 12 window.location.hash = '#' + url; 13 return false; 14 });
在iframe页面(子页面)中,也有一段类似的js,为iframe中的页面超链接绑定事件
1 $('a').click(function(){ 2 var url = $(this).attr('href') 3 if(url && url != '#' && url.indexOf('http') == 0){ 4 var mainHost = window.parent.location.host, 5 i = url.indexOf(mainHost); 6 if(i !== -1){ 7 url = url.substr(i + mainHost.length); 8 } 9 window.parent.location.hash = '#' + url; 10 } 11 return false; 12 });
注意这两段代码,修改的都是父文档(顶层窗口)地址栏的hash值。所以,只需要在父文档中监听onhashchange事件,在事件响应中设置iframe的src 进而load子页面。
1 $container = $('div.page-body > iframe'); 2 window.onhashchange = function(){ 3 $container.attr('src',location.hash.substring(1)); 4 };
iframe高度不能根据内容自适应,需要在子页面load之后刷新iframe的高度
1 var refreshHeight = function(){ 2 var $this = $container, 3 minHeight = $('.page-right').height() - $('.top-menu').height() - 20, 4 contentHeight = $this.contents().find('body').height() + 10; 5 $this.height(contentHeight < minHeight ? minHeight : contentHeight); 6 }; 7 8 $container.load(function(){ 9 refreshHeight(); 10 }); 11 // 首次刷新,否则加载过程中会看到白框 12 refreshHeight();
到这里基本已经实现任意跳转、回退、前进页面不再刷新整个页面。下面的代码是为了解决当打开多个顶层文档时(开多个窗口),音乐不重复播放,方法也很简单,在localStorage中记录顶层文档的数量,每开一个新窗口加1,关闭时减1,只要记录数大于1便不自动播放。
1 if(window.localStorage){ 2 var $window = $(window); 3 $window.on('beforeunload',function(){ 4 console.log('-1'); 5 localStorage.framePage = localStorage.framePage - 1; 6 }); 7 8 window.addEventListener("storage", function(e){ 9 console.log("oldValue: "+ e.oldValue + " newValue:" + e.newValue) 10 }); 11 } 12 var autoplay = location.host !== 'movesun.qq.com'; 13 if(window.localStorage){ 14 if(Number(localStorage.framePage) >= 1){ 15 autoplay = false; 16 } 17 18 if(isNaN(localStorage.framePage) || Number(localStorage.framePage) <= 0) localStorage.framePage = 1; 19 else { 20 localStorage.framePage = Number(localStorage.framePage) + 1; 21 } 22 }
博客依然有两个问题需要解决
1、目前的浏览器已经支持记录iframe变更的历史记录,通过hash记录历史就显的没有必要了。目前网站每次跳转实际产生了两条历史记录。后面找时间移出hash记录或者禁用iframe历史记录
2、考虑到搜索引擎收录的超链接应该是非hash模式的url,比如用户看到的是movesun.com/#/blog/list ,而实际收录的却是movesun.com/blog/list,通过site:movesun.com指令搜索也可以看到
直接访问这类url地址,将直接打开iframe里的内容,所以,当用户直接点击搜索引擎的结果进入博客时,应该将用户跳转到hash模式,页面才能正常显示,但这样对搜索引擎而言,会陷入一个无限循环,影响搜索引擎收录。