前端:將網站打造成單頁面應用SPA
前言
不知你有沒有發現,像Github、百度、微博等這些大站,已經不再使用普通的a標籤做跳轉了。他們大多使用Ajax請求替代了a標籤的預設跳轉,然後使用HTML5的新API修改了Url,你可以在F12的Network皮膚裡發現這個祕密。
這項技術並沒有特別標準的學名,大家都稱呼為Pjax,意為PushState + Ajax。這並不完全準確,因為還有Hash + Ajax等方法,但為了方便,我們下文還是統稱為Pjax。
為什麼要這麼做?
Pjax是一個優秀的解決方案,你有足夠多的理由來使用它:
可以在頁面切換間平滑過渡,增加Loading動畫。
可以在各個頁面間傳遞資料,不依賴URL。
可以選擇性的保留狀態,如音樂網站,切換頁面時不會停止播放歌曲。
所有的標籤都可以用來跳轉,不僅僅是a標籤。
避免了公共JS的反覆執行,如無需在各個頁面開啟時都判斷是否登入過等等。
減少了請求體積,節省流量,加快頁面響應速度。
平滑降級到低版本瀏覽器上,對SEO也不會有影響。
原理呢?
Pjax的原理十分簡單。
1. 攔截a標籤的預設跳轉動作。
2. 使用Ajax請求新頁面。
3. 將返回的Html替換到頁面中。
4. 使用HTML5的History API或者Url的Hash修改Url。
HTML5 History API
我們來看看HTML5在History裡增加了什麼:
history.pushState(state, title, url)
pushState方法會將當前的url新增到歷史記錄中,然後修改當前url為新url。請注意,這個方法只會修改位址列的Url顯示,但並不會發出任何請求。我們正是基於此特性來實現Pjax。它有3個引數:
state: 可以放任意你想放的資料,它將附加到新url上,作為該頁面資訊的一個補充。
title: 顧名思義,就是document.title。不過這個引數目前並無作用,瀏覽器目前會選擇忽略它。
url: 新url,也就是你要顯示在位址列上的url。
history.replaceState(state, title, url)
replaceState方法與pushState大同小異,區別只在於pushState會將當前url新增到歷史記錄,之後再修改url,而replaceState只是修改url,不新增歷史記錄。
window.onpopstate 事件
一般來說,每當url變動時,popstate事件都會被觸發。但若是呼叫pushState來修改url,該事件則不會觸發,因此,我們可以把它用作瀏覽器的前進後退事件。該事件有一個引數,就是上文pushState方法的第一個引數state。
一個例項:
這裡我們以daipig為例,開啟daipig,位址列是http://www.daipig.com 。接下來開啟F12 Console,輸入:
history.pushState({ a: 1, b: 2 }, null, “http://www.daipig.com/abcdefg“);
可以發現,url已經變成我們輸入的url了,但頁面並沒有重新整理,也沒有發出任何請求。現在再輸入history.state,就可以看到我們剛剛傳過來的第一個引數state了。
這時點選後退,url會回到www.daipig.com,同樣是沒有重新整理。只不過後退的時候其實是觸發了window.onpopstate事件的。
詳細文件可以查閱MDN: https://developer.mozilla.org/zh-CN/docs/DOM/Manipulating_the_browser_…
怎麼完整的實現Pjax?
Pjax的原理上文已經講了,並不複雜。我實現了一個比較粗糙的Pjax庫,已經能滿足不少需求,如果你有興趣,可以上Github幫忙完善一下程式碼。地址是:https://github.com/Coffcer/coffce-pjax 。
完整的程式碼見Github,這裡我們只談需要注意的一些地方。
匹配選擇器
要實現Pjax,難免就會有匹配選擇器的需求。你需要判斷當前點選的元素,是否匹配指定選擇器。這裡我給出一個相容至IE8的解決方法:
function matchSelector(element, selector) {
var match =
document.documentElement.webkitMatchesSelector ||
document.documentElement.mozMatchesSelector ||
document.documentElement.msMatchesSelector ||
function(selector, element) {
if (element.tagName === selector.toUpperCase()) return true;
var elements = document.querySelectorAll(selector),
length = elements.length;
while (length--) {
if (elements[length] === this) return true;
}
return false;
};
matchSelector = function(element, selector) {
return match.call(element, selector);
};
return matchSelector(element, selector);
}
matchSelector(document.getElementById(“abc”), “#abc”);
matchSelector(document.querySelector(“a”), “p”);
在現代瀏覽器上,優先使用原生的matchesSelector方法來判斷,在IE8及以下的瀏覽器裡,迴圈document.querySelector的結果集,依次對比。
這個方法利用了閉包,然後重寫自身,只有在第一次呼叫時需要判斷加哪個字首執行哪個方法,其後都是呼叫了閉包的match函式。
不支援HTML5 PushState的瀏覽器怎麼辦?
IE6到IE9是不支援pushState的,要修改Url,只能利用Url的Hash,也即是#號。
你可以隨意找個網站試一下,在url後面加上#號和任意內容,頁面並不會重新整理。此時點選後退也只會回到上一條#號,同樣不會重新整理。
那麼我們只需把pushState(新url)換成localtion.hash = 新url,把onpopstate事件換成onhashchange事件就可以相容IE了。
QQ音樂,網易雲音樂等就是使用這種方式。
現成的庫
我簡單實現了一個比較粗糙的Pjax庫,地址是:https://github.com/Coffcer/coffce-pjax ,歡迎PR和Star。
本文由 Easy 第一時間收藏到GET,原文來自 → segmentfault.com
相關文章
- (轉)前端:將網站打造成單頁面應用SPA前端網站
- vue 單頁應用(spa)前端路由實現原理Vue前端路由
- 前端:你要懂的單頁面應用和多頁面應用前端
- 前端學習之路之SPA(單頁應用)設計原理前端
- 使用Vue.js在WordPress中建立單頁面應用SPAVue.js
- 前端單頁應用SPA時代結束,Hotwire時機已到 - DHH前端
- 單頁面應用和多頁面應用
- 幽默:為單頁應用SPA辯護
- 前端單頁應用微服務化解決方案2 - Single-SPA前端微服務
- 一文讀盡前端路由、後端路由、單頁面應用、多頁面應用前端路由後端
- 前端單頁面應用的許可權管理前端
- 後端說,單頁面SPA和前端路由是怎麼回事後端前端路由
- VUE 單頁面應用 修改頁面titleVue
- 單頁面 Web 應用(Single Page Application,SPA)的工作原理介紹WebAPP
- vonic單頁面應用
- 前端SPA正過渡到MPA多頁應用 - nolanlawson前端
- 單頁應用SPA是個錯誤 - gomakethingsGo
- 前端 SPA 單頁應用資料統計解決方案 (ReactJS / VueJS)前端ReactJSVue
- SPA單頁面應用、前後端分離專案SEO優化的方法後端優化
- 監聽瀏覽器更新URL引數,實現偽SPA單頁面應用瀏覽器
- 原生JavaScript實現的SPA單頁應用(hash路由)JavaScript路由
- Vue SPA(單頁應用)首屏優化實踐Vue優化
- 關於單頁應用(SPA)的經驗之談
- 單頁應用SPA做SEO的一種清奇的方案
- Laravel 配合 puppeteer 抓取 SPA 頁面Laravel
- 如何優化單頁面網站搜尋引擎?優化網站
- 用Web Components構建單頁面應用Web
- 單頁面應用微信分享跳坑指南
- ng中的路由和單頁面應用路由
- 用微前端的方式搭建類單頁應用前端
- SPA設計與架構-理解單頁面Web應用 (埃米頓.A斯科特) 中文pdf掃描版架構Web
- 網站頁面原始碼,前端頁面的最基本組成形式,網頁到底是什麼? 網站開發教程網站原始碼前端網頁
- 如何管理好10萬行程式碼的前端單頁面應用行程前端
- 在vue單頁面應用當中使用sassVue
- webpack+react+antd單頁面應用例項WebReact
- React如何優雅地寫單頁面應用?React
- 大型單頁面應用的進階挑戰
- 前端插拔式 SPA 應用架構實現方案前端應用架構