摘要:是一款可愛的小插件,將和瀏覽器的封裝到一起,解決了單純使用進(jìn)行無刷新加載時(shí)對搜索引擎的不友好,并且節(jié)省了開支提高了瀏覽速度,明顯地優(yōu)化了用戶體驗(yàn)。是提供的,是對瀏覽器歷史對象的增強(qiáng)。
知識要點(diǎn)pjax 是一款可愛的 jQuery 小插件,將 ajax 和瀏覽器的 pushState API 封裝到一起,解決了單純使用 ajax 進(jìn)行無刷新加載時(shí)對搜索引擎的不友好,并且節(jié)省了 HTTP 開支、提高了瀏覽速度,明顯地優(yōu)化了用戶體驗(yàn)。
ajax 自不多說,在這里負(fù)責(zé)攜帶 pjax 標(biāo)識請求后端,將生成好的 html 碎片(注意不是前端取回JSON來進(jìn)行渲染)取回,然后 jQuery 將它替換到 DOM 當(dāng)中。
pushState 是 html5 提供的API,是對瀏覽器歷史對象 history 的增強(qiáng)。了解 Javascript 的都知道 BOM (瀏覽器對象模型),而 window 則是 BOM 的具體實(shí)現(xiàn),history 則是window 的子對象,這個(gè) pushState 就屬于 window.history 的一個(gè)方法。簡單明了。
接下來,我們進(jìn)一步了解一下 pushState 。
先看下面的一段代碼:
var stateObj = { foo: "bar" } history.pushState(stateObj, "title", "bar.html")
首先聲明一個(gè)狀態(tài)對象,能夠儲存任何可序列化數(shù)據(jù),比如將 html 碎片存儲于此,但大小有限制(640k),可以使用 localStorage 等機(jī)制。當(dāng)然也可以不使用,它的取舍我們后面具體實(shí)施時(shí)會提到。
pushState 方法往瀏覽器歷史棧里插入一條歷史項(xiàng),執(zhí)行完成之后,瀏覽器會立即將歷史項(xiàng)中的 url(bar.html) 顯示在地址欄中,(url 接受的是相對地址,會自動補(bǔ)上域名),但不會將其加載。而 "title" 在這里暫時(shí)沒有用處,瀏覽器不會用它來修改頁面標(biāo)題,可以填 null。
那什么時(shí)機(jī)調(diào)用此方法?監(jiān)聽你需要 pjax 效果的超鏈接的 click 事件,禁用默認(rèn)的跳轉(zhuǎn),然后 do that。但講到這里你可能會想到,如果用戶進(jìn)行瀏覽器的前進(jìn)和后退操作,還是會執(zhí)行跳轉(zhuǎn)加載,那該如何處理呢?
這就要用到 pjax 不可忽視的關(guān)鍵角色 -- popstate 事件。這個(gè)事件只在瀏覽器的前進(jìn)和后退操作時(shí)觸發(fā),所以通過監(jiān)聽它,如法炮制上述操作即可 。至此,我們每一次的瀏覽訪問都向搜索引擎伸出了友好的橄欖枝。
講到這里,我們大體了解了 pjax 的流程,就是監(jiān)聽所有需要 pjax 效果的超鏈接,使用 ajax 和后端達(dá)成協(xié)議取回 html 碎片并填充到 DOM 當(dāng)中,pushState 負(fù)責(zé)將瀏覽器地址欄修改成我們想要的 URL,并且往歷史棧中增加一個(gè)歷史項(xiàng),通過監(jiān)聽 popstate,讓瀏覽器的前進(jìn)和后退也 pjax 化。
最后,replaceState 這個(gè) history API 也有必要介紹一下。當(dāng)你需要將當(dāng)前激活的歷史項(xiàng)從歷史棧中徹底抹去并替換成另一個(gè),那用它就對了,使用方法和 pushState 完全一樣,最常見的使用場景是使用 pjax 刷新頁面。
具體實(shí)施pjax 的安裝配置需要前后端配合進(jìn)行。前后端的輪子都有不少現(xiàn)成的,但前端的輪子做不到開箱即用,這是因?yàn)?pjax 的實(shí)現(xiàn)需要結(jié)合項(xiàng)目的具體代碼進(jìn)行實(shí)施,下面我會分別講解。
前端采用最流行的 defunkt/jquery-pjax。話不多說,文檔寫的都很詳細(xì)。這里主要根據(jù)源碼提幾點(diǎn)需要注意的地方:
綁定選擇器時(shí),推薦使用 data-pjax 屬性,這個(gè)屬性會自動尋找標(biāo)簽及其子標(biāo)簽中的超鏈接,綁定 click.pjax (順便注意這里的事件命名空間,目的是為了主動 trigger 時(shí)能區(qū)別對待 click) 事件。
$(document).pjax("[data-pjax] a, a[data-pjax]", "#pjax-container")
jquery-pjax 做到了自動向后兼容,不需要多帶帶做兼容性判斷,放心調(diào)用 pjax 方法即可。
如果你的后端程序響應(yīng)慢,pjax 會不耐煩的直接跳轉(zhuǎn),要么將后端程序或者網(wǎng)絡(luò)環(huán)境優(yōu)化,要么讓 pjax 稍微耐心一點(diǎn):
$.pjax.defaults.timeout = 1600 /*默認(rèn) 650 毫秒*/
之所以說 pjax 不是開箱即用,主要是因?yàn)樗?js 腳本的調(diào)用會在第二次執(zhí)行 pjax 方法時(shí)失效。我剛開始遇到這個(gè)問題時(shí),一頭霧水,折騰了許久而不得解,然后在 laravel china 發(fā)帖求助,很快站長龍哥就站出來,耐心細(xì)致的解答了我的疑惑。仔細(xì)研究了源碼,我發(fā)現(xiàn)了其中兩個(gè)有趣的函數(shù):
var container = extractContainer("", xhr, options) executeScriptTags(container.scripts)
字面意思是將取回的 html 碎片 進(jìn)行加工處理成一個(gè)容器對象,并處理其中的腳本標(biāo)簽,那為什么第一次之后的 pjax 就沒執(zhí)行我的腳本呢?我們繼續(xù)閱讀兩個(gè)函數(shù)體內(nèi)的關(guān)鍵代碼:
extractContainer :
// Gather all script[src] elements obj.scripts = findAll(obj.contents, "script[src]").remove() obj.contents = obj.contents.not(obj.scripts)
將 html 碎片中的所有帶 src 的腳本刪除并儲存在容器對象的 scripts 屬性中,將去除了 scripts 的 html 碎片內(nèi)容賦給 contents 屬性??吹竭@里,你可能會大概明白了 pjax 的用意。繼續(xù)看另一個(gè)函數(shù)體的內(nèi)容:
executeScriptTags :
if (!scripts) return var existingScripts = $("script[src]") scripts.each(function() { var src = this.src var matchedScripts = existingScripts.filter(function() { return this.src === src }) if (matchedScripts.length) return var script = document.createElement("script") var type = $(this).attr("type") if (type) script.type = type script.src = $(this).attr("src") document.head.appendChild(script) })
獲取目前 DOM 中的所有帶 src 的腳本,然后和 html 碎片中的腳本逐個(gè)做比對,如果碎片中有新的腳本就將其插入到 head 標(biāo)簽的最后。啊哈~ pjax 這么做是確保不會重復(fù)請求任何已經(jīng)下載過的腳本文件,節(jié)省 HTTP 開支。但這么做的弊端就是本段開頭說的那個(gè)問題,那如何解決呢?
靈活運(yùn)用 pjax 提供的事件。 要解決上述問題,我們可以監(jiān)聽 pjax:end 事件,當(dāng)然 pjax:success 和 pjax:complete 也行,區(qū)別不大 :
$(document).on("pjax:end", function() { self.blogBootUp() })
當(dāng) pjax 生命周期結(jié)束,主動調(diào)用一下腳本啟動程序即可。這里,我將我所有的腳本啟動程序都封裝到 blogBootUp 中了,具體的代碼請移步我的 Blog 項(xiàng)目。
使用了 pjax ,就相當(dāng)于我們阻斷了瀏覽器的常規(guī)瀏覽機(jī)制,使用相關(guān)接口去重寫瀏覽邏輯。對于瀏覽器的前進(jìn)和后退功能,我們監(jiān)聽了 popstate 事件去使用 pjax,但在常規(guī)的情況下,瀏覽器是有緩存的,所以我們能秒進(jìn)或秒退,但如果 pjax 不優(yōu)化這一塊,那前進(jìn)和后退也要去請求服務(wù)器的話會付出不少的代價(jià)。查看 pjax 源碼發(fā)現(xiàn),作者是做了緩存處理的,通過兩個(gè)關(guān)鍵函數(shù) cachePush 和 cachePop 來模擬瀏覽器緩存,只不過是存在數(shù)組中(也就是內(nèi)存中),如果你想達(dá)到 真正的 webApp 的水準(zhǔn),我覺得還需要配合 localStorage 和 WebSocket 等類似的機(jī)制來穩(wěn)健的存儲數(shù)據(jù)和靈活的控制頁面的緩存時(shí)間,當(dāng)然這就比較復(fù)雜了,待以后再去實(shí)踐好了。
后端我選用了 JacobBennett/pjax。兼容 laravel 5.* ,采用了中間件的形式,所以使用起來很簡單,直接將中間件引入到 app/Http/Kernel.php 中即可。讀了下源碼,發(fā)現(xiàn)無非是使用了一個(gè) DOM 爬蟲根據(jù)客戶端 header 傳遞的 pjax 標(biāo)識和 pjax 容器標(biāo)識 (就是 selector),抓取 laravel 響應(yīng)對象內(nèi)容中的 title 和 容器內(nèi)容,然后連綴在一起復(fù)寫回去,返回給客戶端。
最后,再推薦使用一個(gè)加載效果動畫的 javascript 插件,配合 pjax 使用毫無違和感。rstacruz/nprogress,文檔寫的很簡潔明了,有專門針對 pjax 的使用說明,用上之后,還是相當(dāng)酷炫的。
參考資料在 Laravel 應(yīng)用中使用 pjax 進(jìn)行頁面加速
PJAX的實(shí)現(xiàn)與應(yīng)用
HTML5 Page Cache with pjax + Web Storage + Firebase
defunkt/jquery-pjax
JacobBennett/pjax
原文鏈接:https://macken.me/article/speed-up-your-website-with-pjax
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/22097.html
摘要:是一款可愛的小插件,將和瀏覽器的封裝到一起,解決了單純使用進(jìn)行無刷新加載時(shí)對搜索引擎的不友好,并且節(jié)省了開支提高了瀏覽速度,明顯地優(yōu)化了用戶體驗(yàn)。是提供的,是對瀏覽器歷史對象的增強(qiáng)。 showImg(https://segmentfault.com/img/remote/1460000007640529?w=1008&h=569); pjax 是一款可愛的 jQuery 小插件,將 a...
摘要:最重要的就是找一個(gè)適合自己的主題了。事實(shí)上,免費(fèi)主題也非常多,而且很多的免費(fèi)主題在功能上和界面美觀上已經(jīng)大大超過了付費(fèi)的主題。加上這些主題都是開源的,基本上可以在上找得到源碼,安全性是沒有問題,主題的作者也在不斷更新當(dāng)中。WordPress最重要的就是找一個(gè)適合自己的主題了。好一點(diǎn)的WordPress主題基本上都是要收費(fèi)的,而且價(jià)格還不便宜,這導(dǎo)致了不少的新手朋友們很為難。而有時(shí)我們僅僅根據(jù)...
摘要:初步理解如果最近打電話給武漢的小伙伴,他說信號不好,那么相信我,他肯定不是真的信號不好,也不是不想和你說話,而是他可能在冰箱里。。。 初步理解 如果最近打電話給武漢的小伙伴,他說信號不好,那么相信我,他肯定不是真的信號不好,也不是不想和你說話,而是他可能在冰箱里。。。武漢的天氣從來都是喜怒無常的,是吧,屌絲氣十足,今年也是絲毫看不出有任何逆襲的跡象和可能性,當(dāng)然咱也沒必要去操那個(gè)心;好...
摘要:項(xiàng)目概述糖果盒子是采用開發(fā)的站點(diǎn)導(dǎo)航應(yīng)用,專注分享優(yōu)質(zhì)開發(fā)資源站點(diǎn),希望成為開發(fā)人員最喜愛的的書簽導(dǎo)航。線上地址糖果盒子開發(fā)者的書簽導(dǎo)航地址求環(huán)境要求部署安裝本項(xiàng)目代碼使用框架開發(fā),本地開發(fā)環(huán)境使用。 showImg(https://segmentfault.com/img/remote/1460000012018405?w=3346&h=1950);showImg(https://s...
摘要:項(xiàng)目概述糖果盒子是采用開發(fā)的站點(diǎn)導(dǎo)航應(yīng)用,專注分享優(yōu)質(zhì)開發(fā)資源站點(diǎn),希望成為開發(fā)人員最喜愛的的書簽導(dǎo)航。線上地址糖果盒子開發(fā)者的書簽導(dǎo)航地址求環(huán)境要求部署安裝本項(xiàng)目代碼使用框架開發(fā),本地開發(fā)環(huán)境使用。 showImg(https://segmentfault.com/img/remote/1460000012018405?w=3346&h=1950);showImg(https://s...
閱讀 3057·2021-09-22 14:59
閱讀 1885·2021-09-22 10:02
閱讀 2120·2021-09-04 16:48
閱讀 2269·2019-08-30 15:53
閱讀 2972·2019-08-30 11:27
閱讀 3414·2019-08-29 18:35
閱讀 969·2019-08-29 17:07
閱讀 2678·2019-08-29 13:27