如何快速開發SPA應用

前言

web早已經進入了2.0時代了,現在的網頁大有往系統應用級別的方向發展的趨勢,不再是之前的簡單展現信息的界面了。現在不少webapp已經作到了原生應用的功能,而且運用自身的優點逐步取代之。HTML5也很給力,對多平臺,多屏幕設備的良好兼容性使得前端工程師們在各類平臺上大顯身手。滷煮兩年前進公司接到的也是一個SPA應用的項目,也很有些本身的心得,今日就寫篇博文,與你們分享下。javascript

SPA

單頁 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應用而生的,它們優缺點滷煮就不在此一一提了。

hash值

在此處提到一個比較重要的概念: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> 

SEO優化

因爲咱們在處理單頁應用的時候頁面是不刷新的,因此會致使咱們的網頁記錄和內容很難被搜索引擎抓取到。搜索引擎抓取頁面首先要遵循http協議,但是#不是協議內的內容。而實際上也是這樣,咱們沒有見過搜索引擎的搜索結果中,哪一條記錄能夠快速定位到網頁內的某個位置的。解決的方法是用 #!號代替#號,由於谷歌會抓取帶有#!的URL。(Google規定,若是你但願Ajax生成的內容被瀏覽引擎讀取,那麼URL中可使用"#!"(這種URL在通常頁面通常不會產生定位效果)),這樣咱們能夠解決ajax的不被搜索引擎抓取的問題。在vueJs裏面,咱們能夠看到做者就是這樣作的。

結束

以上就是利用hash原理實現的一個很簡單的SPA。固然,要實現項目中的單頁應用,還有不少工做要作。好比傳參,動畫,異步資源加載的問題都是須要解決的。滷煮此處只是示範了一個很簡單的例子,但願在作不復雜又不想引入其餘框架的同窗提供一點思路。另外單頁雖好,但不要亂用哦,根據項目的具體需求制定對應的解決方案而不是一味的追潮逐流。

參考資料

搜索引擎會不會抓取帶#號(哈希值)的URL

基於MVC的JavaScript Web富應用開發

URL的井號

相關文章
相關標籤/搜索