web早已經進入了2.0時代了,現在的網頁大有往系統應用級別的方向發展的趨勢,不再是之前的簡單展現信息的界面了。現在不少webapp已經作到了原生應用的功能,而且運用自身的優點逐步取代之。HTML5也很給力,對多平臺,多屏幕設備的良好兼容性使得前端工程師們在各類平臺上大顯身手。滷煮兩年前進公司接到的也是一個SPA應用的項目,也很有些本身的心得,今日就寫篇博文,與你們分享下。javascript
單頁 Web 應用 (single-page application 簡稱爲 SPA) 是一種特殊的 Web 應用。它將全部的活動侷限於一個Web頁面中,僅在該Web頁面初始化時加載相應的HTML、JavaScript 和 CSS。一旦頁面加載完成了,SPA不會由於用戶的操做而進行頁面的從新加載或跳轉。取而代之的是利用 JavaScript 動態的變換HTML的內容,從而實現UI與用戶的交互。因爲避免了頁面的從新加載,SPA 能夠提供較爲流暢的用戶體驗。得益於ajax,咱們能夠實現無跳轉刷新,又多虧了瀏覽器的histroy機制,咱們用hash的變化從而能夠實現推進界面變化。從而模擬元素客戶端的單頁面切換效果:css
SPA被人追捧是有道理的,可是它也有不足之處。固然任何東西都有兩面性,如下是滷煮總結的一些目前SPA的優缺點:html
優勢:前端
1.無刷新界面,給用戶體驗原生的應用感受vue
2.節省原生(android和ios)app開發成本java
3.提升發佈效率,無需每次安裝更新包。這個對於ios開發人員來講印象尤爲深吧。android
4.容易藉助其餘知名平臺更有利於營銷和推廣ios
5.符合web2.0的趨勢web
缺點:ajax
1.效果和性能確實和原生的有較大差距
2.各個瀏覽器的版本兼容性不同
3.業務隨着代碼量增長而增長,不利於首屏優化
4.某些平臺對hash有偏見,有些甚至不支持pushstate。(你懂的)
5.不利於搜索引擎抓取
一種開發模式火起來以後,對應的框架會隨之而起。像近幾年比較火的angularJS,是目前中最流行的mvvm框架,很是適合作SPA;與之相似的還有vueJS,滷煮在項目中也用過,相對於前者比較輕量。還有早一些的backbone,提供最基本的mvc模式,其餘的模塊大小和細節得本身決定。最先接觸的應該是extjs吧,這頭超級巨無霸在很早的時候被用來建立企業後臺應用,現在也跟着時代的變化作出了不少的改進。等等這些框架也好,庫也好,都旨在爲了更好的構建SPA應用而生的,它們優缺點滷煮就不在此一一提了。
在此處提到一個比較重要的概念:URL中的井號。其實它只是瀏覽地址中的一個特殊符號。在之前,咱們常常用它來定位文檔位置。例如如下代碼:
<a href="target">go target</a> ...... <div id="target">i am target place</div>
點擊a連接,文檔會滾動到id爲target的div的可視區域上面去。hash除了這個功能還有另外一一種含義:指導瀏覽器的行爲但不上傳到服務器。你們都知道,改變url中的任何一個字符都會致使瀏覽器從新請求服務器,除了#號後面那段字符以外。因此,簡而言之咱們能夠這樣理解:改變#後面的值不觸發網頁重載,但會記錄到瀏覽器history中去。
實現SPA的方法有不少,終歸一種遵循一種原則,界面無刷新。若是要實現原生應用中相似許多不一樣頁面切換的效果,咱們採用的是div切換顯示和隱藏。而驅動div顯示隱藏的方式有不少種
1.監聽地址欄中hash變化驅動界面變化
2.用pushsate記錄瀏覽器的歷史,驅動界面發送變化
3.直接在界面用普通事件驅動界面變化
前兩種方式較爲廣泛,由於它們的變化記錄瀏覽器會保存在history中,能夠經過回退/前進按鈕找回,或者history對象中的方法控制。最後一種方法是用普通事件驅動的,沒有改變瀏覽器的history對象,因此一旦用戶按了返回按鈕將會退到瀏覽器的主界面。因此,通常採用前兩種方式。值得一提的是,在不支持hash監聽和pushsate變化的瀏覽器中能夠考慮用延時函數,不停得去監聽瀏覽器地址欄中url發生的變化,從而驅動界面變化。
滷煮在好久以前的一篇博文用pushstate的變化作了一個小小的示例,你們能夠在以前的博文中找到它。在這裏,咱們用監聽hash變化的方式展現SPA是怎麼樣運行工做的,同時從零開始,搭建一個基礎的SPA。幫助你們理解簡單的單界面應用的原理。
首先,咱們畫出三個div,它們其實是做爲三個界面存在界面上的,body做爲界面外框容器,限制着它們的大小。爲了給每一個界面配對一個hash地址,咱們給每一個div配一個id,講hash地址與對應的選擇器(id、class)創建連接關係,從而能夠從hash變化值中操做界面。
<body> <div id="A" class="a J-A">A</div> <div id="B" class="b J-B">B</div> <div id="C" class="c J-C">C</div> </body>
接下來,爲它們添加樣式,每一個div都是全屏的,一開始只有A界面顯示,其餘的都隱藏之:
body { height: 500px; width: 100%; margin: 0; padding: 0; } div { width: 100%; height: 100%; position: absolute; font-size: 500px; text-align: center; display: none; } .a { background-color: pink; display: block; } .b { background-color: red; } .c { background-color: gray; }
如今咱們給網頁添加上行爲,首先須要知道的一點是,hash指即地址欄中#號後面的字符串,它的改變不會引發界面的刷新,可是會出發onhashchange事件,咱們要作的就是監聽這個事件:
function hashChanged(hashObj) { //變化以後的url var newhash = hashObj.newURL.split('#')[1]; //變化以前的url var oldhash = hashObj.oldURL.split('#')[1]; //將對應的hash下界面顯示和隱藏 document.getElementById(oldhash).style.display = 'none'; document.getElementById(newhash).style.display = 'block'; } //監聽路由變化 window.onhashchange = hashChanged;
目前,只須要以上的代碼,咱們即可以完成一個最簡單的SPA,經過地址欄的變化,界面會相應地變化。固然,除了手動在地址欄裏面改變hash的變化,咱們也能夠用代碼改變它的變化,從而推進界面變化,下面是兩種方式的效果圖:
能夠看到,左邊是在瀏覽器中直接修改hash引發了界面的變化,而右邊則是經過代碼控制界面變化。這兩種方式均可以在history中留下痕跡,從而當咱們店家回退/前進按鈕的時候追溯到以前的界面去。有些平臺不容許咱們去手動修改地址欄,(好比微信),那麼咱們通常採用第二種方式便可。何況,比較少的用戶會去修改地址欄。
下面貼出全部的代碼:
<!DOCTYPE html> <html> <head> <title></title> <style type="text/css"> body { height: 500px; width: 100%; margin: 0; padding: 0; } div { width: 100%; height: 100%; position: absolute; font-size: 500px; text-align: center; display: none; } .a { background-color: pink; display: block; } .b { background-color: red; } .c { background-color: gray; } </style> </head> <body> <div id="A" class="a J-A">A</div> <div id="B" class="b J-B">B</div> <div id="C" class="c J-C">C</div> </body> <script type="text/javascript"> function hashChanged(hashObj) { //變化以後的url var newhash = hashObj.newURL.split('#')[1]; //變化以前的url var oldhash = hashObj.oldURL.split('#')[1]; //將對應的hash下界面顯示和隱藏 document.getElementById(oldhash).style.display = 'none'; document.getElementById(newhash).style.display = 'block'; } //監聽路由變化 window.onhashchange = hashChanged; </script> </html>
因爲咱們在處理單頁應用的時候頁面是不刷新的,因此會致使咱們的網頁記錄和內容很難被搜索引擎抓取到。搜索引擎抓取頁面首先要遵循http協議,但是#不是協議內的內容。而實際上也是這樣,咱們沒有見過搜索引擎的搜索結果中,哪一條記錄能夠快速定位到網頁內的某個位置的。解決的方法是用 #!號代替#號,由於谷歌會抓取帶有#!的URL。(Google規定,若是你但願Ajax生成的內容被瀏覽引擎讀取,那麼URL中可使用"#!"(這種URL在通常頁面通常不會產生定位效果)),這樣咱們能夠解決ajax的不被搜索引擎抓取的問題。在vueJs裏面,咱們能夠看到做者就是這樣作的。
以上就是利用hash原理實現的一個很簡單的SPA。固然,要實現項目中的單頁應用,還有不少工做要作。好比傳參,動畫,異步資源加載的問題都是須要解決的。滷煮此處只是示範了一個很簡單的例子,但願在作不復雜又不想引入其餘框架的同窗提供一點思路。另外單頁雖好,但不要亂用哦,根據項目的具體需求制定對應的解決方案而不是一味的追潮逐流。