SPA(單頁面應用)
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模式,頁面才能正常顯示,但這樣對搜尋引擎而言,會陷入一個無限迴圈,影響搜尋引擎收錄。
總結:SPA(單頁面web應用)和MPA(多頁面web應用)的區別