什么是單頁應(yīng)用呢?單頁應(yīng)用的全稱是 single-page application,簡(jiǎn)稱 SPA,它是一種網(wǎng)站應(yīng)用的模型,它可以動(dòng)態(tài)重寫當(dāng)前的頁面來與用戶交互,而不需要重新加載整個(gè)頁面。單頁應(yīng)用的流暢性讓 Web 應(yīng)用更像桌面端或 Native 應(yīng)用了。相對(duì)于傳統(tǒng)的 Web 應(yīng)用,單頁應(yīng)用做到了前后端分離,后端只負(fù)責(zé)處理數(shù)據(jù)提供接口,頁面邏輯和頁面渲染都交給了前端。前端發(fā)展到現(xiàn)在,單頁應(yīng)用的使用已經(jīng)很廣泛,目前時(shí)興的 react、vue、Angular 等前端框架均采用了 SPA 原則。單頁應(yīng)用意味著前端掌握了主動(dòng)權(quán),也讓前端代碼更復(fù)雜和龐大,模塊化、組件化以及架構(gòu)設(shè)計(jì)都變得越來越重要。
2、工作原理
SPA 的一個(gè)重要實(shí)現(xiàn)就是改變路由時(shí),頁面不刷新。實(shí)現(xiàn)這個(gè)功能,通常有兩種方式:使用 window.history 對(duì)象或 location.hash。
3、history 對(duì)象
window.history 包含了瀏覽器的歷史信息,它有以下幾種常用方法:
history.back():與在瀏覽器點(diǎn)擊后退按鈕相同;
history.forward():與在瀏覽器中點(diǎn)擊按鈕向前相同;
history.go(n):接受一個(gè)整數(shù)作為參數(shù),移動(dòng)到該整數(shù)指定的頁面,比如 go(1) 相當(dāng)于 forward(),go(-1) 相當(dāng)于 back(),go(0) 相當(dāng)于刷新當(dāng)前頁面。
html5 對(duì) history 對(duì)象新增了 pushState() 和 replaceState() 方法,這兩個(gè)方法可以往歷史棧中添加數(shù)據(jù),給用戶的感覺就是瀏覽器的 url 改變了,但是頁面并沒有重新加載。pushState() 是在瀏覽記錄中添加一個(gè)新記錄,replaceState() 則是修改當(dāng)前的瀏覽器記錄,這是二者的細(xì)微差別,使用時(shí)參數(shù)的字段和含義都是一樣的。
window.history.pushState(state, title, url)
state 是狀態(tài)對(duì)象,可以用 history.state 讀??;title 表示新頁面的標(biāo)題,但是現(xiàn)在的所有瀏覽器都會(huì)忽略這個(gè)字段,所以可以傳 null;url 是新頁面的地址,必須是和當(dāng)前頁面在同一個(gè)域。當(dāng)用戶點(diǎn)擊瀏覽器上的前進(jìn)和后退按鈕時(shí),或者調(diào)用上述 window.history 的 back、forward 和 go 方法,就會(huì)觸發(fā) popstate 事件。該事件只針對(duì)同一個(gè)文檔,如果瀏覽歷史的切換導(dǎo)致加載了不同的文檔,popstate 事件將不會(huì)被觸發(fā)。popstate 事件回調(diào)函數(shù)的參數(shù)中的 state 屬性指向 pushState() 和 replaceState() 方法為當(dāng)前頁面提供的狀態(tài),也就是 pushState() 和 replaceState() 方法使用時(shí)傳的第一個(gè)參數(shù) state。
4、hash
hash 是 location 對(duì)象的屬性,它指的是當(dāng)前 url 的錨,也就是從 # 號(hào)開始的部分。修改 location.hash 并監(jiān)聽 window 的 hashchange 事件,也能達(dá)到同樣的目的。
5、優(yōu)缺點(diǎn)
SPA 可圈可點(diǎn),現(xiàn)在已被大家廣泛使用,也得到了主流框架的支持,但是它也有局限性,我們將它的優(yōu)缺點(diǎn)總結(jié)如下。
優(yōu)點(diǎn):
無刷新體驗(yàn),用戶在切換頁面過程中不會(huì)頻繁被“打斷”,因?yàn)榻缑婵蚣芏荚诒镜?,?duì)用戶的響應(yīng)非常及時(shí),因此提升了用戶體驗(yàn);
分離前后端關(guān)注點(diǎn),前端負(fù)責(zé)界面顯示,后端負(fù)責(zé)數(shù)據(jù)存儲(chǔ)和計(jì)算,各司其職,不會(huì)把前后端的邏輯混雜在一起;
減輕服務(wù)器壓力,服務(wù)器只用出數(shù)據(jù)就可以,不用管展示邏輯和頁面合成,吞吐能力會(huì)提高幾倍;
API 共享,同一套后端程序代碼,不用修改就可以用于Web界面、手機(jī)、平板等多種客戶端;
完全的前端組件化,前端開發(fā)不再以頁面為單位,更多地采用組件化的思想,代碼結(jié)構(gòu)和組織方式更加規(guī)范化,便于修改和調(diào)整。
缺點(diǎn):
對(duì) seo 不友好,盡管可以通過 Prerender 預(yù)渲染優(yōu)化等技術(shù)解決一部分,但是相對(duì)還是不容易索引到它;易出錯(cuò),需要使用程序管理前進(jìn)、后退、地址欄等信息;
缺失URL地址,搜索引擎程序無法正確或完整索引整站或頁面完整內(nèi)容。無法分享轉(zhuǎn)發(fā)內(nèi)容對(duì)應(yīng)URL地址等;
6、對(duì)比分析
|
單頁面應(yīng)用(SinglePage Web Application,SPA) | 多頁面應(yīng)用(MultiPage Application,MPA) |
---|---|---|
組成 | 一個(gè)外殼頁面和多個(gè)頁面片段組成 | 多個(gè)完整頁面構(gòu)成 |
資源共用(css,js) | 共用,只需在外殼部分加載 | 不共用,每個(gè)頁面都需要加載 |
刷新方式 | 頁面局部刷新或更改 | 整頁刷新 |
url 模式 |
a.com/#/pageone a.com/#/pagetwo |
a.com/pageone.html a.com/pagetwo.html |
用戶體驗(yàn) | 頁面片段間的切換快,用戶體驗(yàn)良好 | 頁面切換加載緩慢,流暢度不夠,用戶體驗(yàn)比較差 |
轉(zhuǎn)場(chǎng)動(dòng)畫 | 容易實(shí)現(xiàn) | 無法實(shí)現(xiàn) |
數(shù)據(jù)傳遞 | 容易 | 依賴 url傳參、或者cookie 、localStorage等 |
搜索引擎優(yōu)化(SEO) | 需要單獨(dú)方案、實(shí)現(xiàn)較為困難、不利于SEO檢索 可利用服務(wù)器端渲染()優(yōu)化 | 實(shí)現(xiàn)方法簡(jiǎn)易 |
試用范圍 | 高要求的體驗(yàn)度、追求界面流暢的應(yīng)用 | 適用于追求高度支持搜索引擎的應(yīng)用 |
開發(fā)成本 | 較高,常需借助專業(yè)的框架 | 較低 ,但頁面重復(fù)代碼多 |
維護(hù)成本 | 相對(duì)容易 | 相對(duì)復(fù)雜 |