simple-spa 一個簡單的單頁應用實例

上兩篇文章說過要寫一個簡單的單頁應用例子的, 遲遲沒有兌諾, 實在有愧 哈哈。這篇寫給小白用戶哈。html

正好趁今天風和日麗,事情很少, 把從項目裏的代碼扣取了一下, 整理了一個簡單的例子。
由於咱們生產項目用到es6 還有構建工具,爲了讓例子足夠簡單和原生,除了一個zepto,連require都是我以前寫的文章裏的實現的,很簡單70行代碼。html5

事例地址

github:https://github.com/skyweaver213/simple-spagit

demo: https://skyweaver213.github.io/simple-spa/example/demo/index.htmles6

 

單頁應用

單頁應用是指在瀏覽器中運行的應用,它們在使用期間不會從新加載頁面。像全部的應用同樣,它旨在幫助用戶完成任務,好比「編寫文檔」或者「管理Web服務器」。能夠認爲單頁應用是一種從Web服務器加載的富客戶端。(摘自:http://blog.csdn.net/zuoninger/article/details/38842823github

 

如何實現

單頁應用 說白了就是不用刷新頁面,例如首頁點擊 查詢到 列表頁,通常給一個過場動畫,從而優化用戶的體驗。可是每每單頁應用首次加載時間比較長,由於不少人每每把代碼都打進首屏裏了。web

而後能不能作到既要極速打開首屏體驗,也要保留單頁的流程用戶體驗呢?ajax

這時候  極致的按需, 能把這個問題 迎刃而解。api

截圖爲例, 例如綠色的其實就是用戶打開這個頁面首先最關心的部分,咱們把這部分納入到首屏裏,而後待首屏渲染成功後,再異步 require 其餘首屏之額的模塊,例如我藍色框住的信息和操做。瀏覽器

例如這樣。服務器

        //航班卡片渲染
        flightInfo.render(orderDetail, orderDetailInfo);
        //價格render
        pricetotal.render(orderDetail, orderDetailInfo);
    
        //異步模塊加載  
        this.loadAsynModules();
        this.loadAsynXprdModules();    

 

一、單頁的核心實現

 

利用了瀏覽器 支持歷史記錄操做,pushState 和popstate (若是不支持這個屬性的瀏覽器能夠用hashchange這個事件代替,移動端基本都支持了)

沒了解過這個屬性的 能夠看看 張鑫旭的 :http://www.zhangxinxu.com/wordpress/2013/06/html5-history-api-pushstate-replacestate-ajax/

流程大概是這樣

拉取html:

 1                 var me = this;
 2                 //獲取html
 3                 $.ajax({
 4                     url: htmlPath,
 5                     type: 'get',
 6                     success: function (data) {
 7                         //console.log('ss data ', data);
 8                         getScript(data, me);
 9                     },
10                     error: function (e) {
11                         console.log('error ', e);
12                     }
13 
14                 });

 

html 內容:

 1 <div class="main-viewport">
 2     <div class="viewport index-viewport" data-no-init="true">
 3         <style>
 4             .index-viewport {
 5                 background: cornflowerblue;
 6             }
 7         </style>
 8 
 9         <div class="viewport-content">
10             index
11 
12             <button class="js_tolist"> to list </button>
13         </div>
14 
15 
16         <script type="text/fscript" data-src="./js/index.js" ></script>
17     </div>
18 </div>

注意 script是 data-src ,由於是單頁代碼動態引入,而後拉取html成功後 會自動關聯viewport下的script,再讀取data-src的內容,而後動態引入script資源。

 

動態引入js:

 1                 //取scripts的絕對路徑 若是不是絕對路徑 ,須要動態計算
 2                 var script_path = scripts[0].getAttribute('data-src') || '';
 3 
 4                 require([script_path], function (exports) {
 5 
 6                     var view = cloneObj(exports.view);
 7 
 8                     view.htmlPath = htmlPath;
 9                     view.viewPort = $("[page-path='" + htmlPath + "']");
10                     view.$ = function (selector) {
11                         return view.viewPort.find(selector);
12                     };
13                     //每建立一個view 把引用存起來
14                     DF.VIEWREF[htmlPath] = view;
15 
16                     //若是上一個view存在
17                     scope.preView && scope.preView.onHide.apply(scope.preView);
18 
19                     //加載頁面完成後
20                     scope.fishLoad(htmlPath, pushState, search);
21 
22 
23                     view && view.onCreate.apply(view);
24                     //綁定事件
25                     if (view && view.events) {
26                         DF.bindEventAction.call(view, view.events);
27                     }
28 
29 
30                     view && view.onShow.apply(view);
31                     if (view.onAppear) {
32                         DF.onAppear = view.onAppear;
33                     }
34                     scope.updatePageid(view);
35 
36 
37                 });

js動態引入成功後,再執行單頁的生命週期。

分別是

onCreate 頁面建立的時候執行, 只執行一次

onShow  頁面建立後會執行, 每次切換下一個頁面會執行

onHide   頁面切換的時候,上一個頁面隱藏的時候會執行

 

 

 而後資源加載成功,頁面渲染成功後, 會pushSate,修改瀏覽器地址

 1     //fishLoad
 2     fishLoad: function (htmlPath, pushState, search) {
 3         //記錄上一個頁面
 4         if (this.curView) {
 5             this.preView = this.curView;
 6         } else {
 7             this.preView = DF.VIEWREF[htmlPath];
 8         }
 9 
10         search = search || '';
11 
12         //設置當前view
13         this.curView = DF.VIEWREF[htmlPath];
14 
15         //console.log('curview ', this.curView.htmlPath, ' preView ', this.preView.htmlPath)
16 
17         //渲染成功
18         if (pushState) {
19             history.pushState({
20                 'viewPath': htmlPath,
21                 'pro': 'spa',
22                 'url': htmlPath
23             }, "頁面標題", htmlPath + search);
24         }
25 
26     },

 

back回退的:

 1        back: function () {
 2            history.back();
 3        },
 4 
 5         //history.back 會觸發 popstate,而後會從新走loadView邏輯
 6         $(window).bind("popstate.pageview", function (e) {
 7 
 8             //是單頁spa操做  才觸發
 9             if (this.getViewPath(location.href) != this.curView.htmlPath) {
10                 /*
11                  * 該幹嗎幹嗎
12                  */
13                 var pat = new RegExp(FHYBRID.appPath, 'i');
14                 DF.loadView(location.pathname.replace(pat, ''), false, false, true);
15             }
16 
17         }.bind(this));

 

 

動畫過渡:

 1     //動畫執行方法
 2     /**
 3      * @param animInitClass  動畫初始化時候的class
 4      * @param animBeforeClass  動畫執行前的class
 5      * @param animEndClass  動畫執行後的class
 6      */
 7     viewAnimation: function (opt) {
 8         var $static_el = opt.staticView;
 9         var $anim_el = opt.animView;
10         var $anim_init_class = opt.animInitClass;
11         var $anim_before_class = opt.animBeforeClass;
12         var $anim_end_rmclass = opt.animEndClass;
13         var anim_type = opt.animType || 'in';  //動畫是進入 仍是  出  ;  in  or out
14 
15         $anim_el.addClass($anim_init_class);
16 
17         //進入 動畫節點 顯示, 出, 對上一個頁面顯示
18         (anim_type == 'in') ? $anim_el.show() : $static_el.show();
19 
20 
21         setTimeout(function () {
22             $anim_el.addClass($anim_before_class);
23 
24             $anim_el.on('webkitTransitionEnd', function () {
25                 //進入 對上一個頁面隱藏;  出 動畫節點 隱藏,
26                 (anim_type == 'in') ? $static_el.hide() : $anim_el.hide();
27 
28                 $anim_el.removeClass($anim_end_rmclass);
29                 $anim_el.off('webkitTransitionEnd');
30             });
31         }, 0);
32 
33 
34     },

調用:

1                     //切換動畫
2                     this.viewAnimation({
3                         staticView: this.preView.viewPort,
4                         animView: this.curView.viewPort,
5                         animType: 'in',
6                         animInitClass: 'ui-page-right ui-transition',
7                         animBeforeClass: 'ui-page-center-i',
8                         animEndClass: 'ui-page-right ui-transition ui-page-center-i'
9                     });

 

最後  請看我上面的事例,一共三個頁面:

index.html
list.html
booking.html

首先執行 index onCreate -> 而後執行 index onShow ;點擊tolist; 執行 index onHide 而後執行 list onCreate-> list onShow;

相關文章
相關標籤/搜索