動手實現一個單頁面應用

前言

在單頁面應用程序中,先後端採用了徹底分離的方法,所以在前端實現路由的切換很是的重要。同時前端實現路由能夠減小請求數,緩解後端的壓力。在單頁面中的路由主要有兩種實現方法,一種是經過h5的history api來實現,還有一種是hash來實現。javascript

history

history的方法主要是應用了幾個H5的histroy API:css

API

  • window.pushState(stateData, title, url)

在history中建立一個新的訪問記錄,不能跨域,且不形成頁面刷新,stateData爲當前頁面的一些信息在觸發popstate事件的時候能夠調用;title爲網頁的標題;url是瀏覽器中顯示的網址。html

  • window.replaceState(stateData, title, url)

修改當前的訪問記錄,不能跨域,且不形成頁面刷新前端

  • windows事件popstate

當回到頁面前一頁或者後一頁的時候觸發java

API示意圖

image
這張圖表示了這些api的具體操做。jquery

pushState主要就是向當前頁面的後面添加一個新的頁面,新的頁面地址是第三個參數。
replaceState是把當前頁面的地址替換成他的第三個參數,history並不會記錄以前的地址
這兩個方法都不能進行頁面的刷新,因此須要在調用方法的時候執行一下相關路由變化的操做,從而達到頁面不切換的效果更改頁面的單頁面效果。

舉個栗子

所以只要在頁面跳轉的時候改爲pushState而後執行當前頁面的加載函數,就能夠實現不刷新的頁面跳轉了如:
原來地址是:https://www.baidu.com
而後點擊了按鈕進行頁面跳轉就執行函數:
history.pushState(data, title, 'https://www.baidu.com/test')
而後瀏覽器上面的地址已經變成了https://www.baidu.com/test而後同時還有返回的按鈕
而後調用一下test頁面的渲染方法

存在問題

這樣子會存在一個問題,就是當頁面直接打開https://www.baidu.com/test服務器會找不到這個頁面而報404的錯git

解決問題

對此錯誤我想到了兩種解決方法github

  • 其一是你在服務器端設置一下當訪問https://www.baidu.com/如下的全部地址所有轉發到https://www.baidu.com/這個地址上來。而後前端經過對url的解析來肯定用戶想訪問的頁面,而後渲染這個頁面
  • 其二是你跳轉頁面用不一樣參數的形式來跳轉頁面,如windows

    原來地址是:https://www.baidu.com後端

    而後點擊跳轉的時候執行

    history.pushState(data, title, '?page=test')

    而後瀏覽器地址欄的地址是https://www.baidu.com?page=test

    而後當你訪問https://www.baidu.com?page=test頁面的時候會打開https://www.baidu.com而後你能夠根據page參數的值來判斷用戶想訪問的頁面,進行不一樣的渲染

使用瞭解決方法二寫的一個history單頁面的小栗子

html代碼
<div id="msg"></div>
<br><br>
<span class="msgBtn" data-route='A'>toFirst</span>
<span class="msgBtn" data-route='B'>toSecond</span>
<span class="msgBtn" data-route='C'>toThird</span>

樣式很簡單就是一個顯示信息的msg而後三個按鈕對應不一樣的頁面
image

js代碼
//路由註冊
    const message = {
        undefined: 'massage',
        A: 'hahahh',
        B: 'hehehehe',
        C: 'hohohoho'
    };

    //頁面分發加載
    function showMsg(el) {
        const _loc = location.href;
        let url_msg_id = GetRequest(_loc);
        el.html(message[url_msg_id.msg]);
    }

    //監聽前進後退按鈕
    window.addEventListener('popstate', function (e) {
        showMsg($('#msg'));
    });

    //點擊切換路由
    $('.msgBtn').click(function (e) {
        console.log(e.currentTarget );
        history.pushState(null, null, '?msg=' + e.target.dataset.route);
        showMsg($('#msg'));
    });

    //處理url
    function GetRequest() {
        let url = location.search; //獲取url中"?"符後的字串
        let theRequest = {};
        if (url.indexOf("?") !== -1) {
            let str = url.substr(1);
            let strs = str.split("&");
            for (let i = 0; i < strs.length; i++) {
                theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
            }
        }
        return theRequest;
    }

    //頁面加載渲染
    showMsg($('#msg'));
  • 在註冊路由裏面的這個對象是每一個路由名對應不一樣的信息,而後若是是頁面的話能夠每一個路由名對應不一樣頁面的渲染方法,其中undefined是沒有參數的時候的頁面。
  • showMsg方法在頁面加載和路由切換的時候執行,做用是根據當前的地址來渲染不一樣的頁面,這裏只是用了$().html()來更改頁面,若是是不一樣的頁面的話能夠來執行不一樣的渲染方法
  • 監聽popstate的做用是點擊瀏覽器的前進後退的時候執行一下渲染方法
  • 點擊事件是點擊不一樣的按鈕來更改地址而後觸發渲染方法
  • GetRequest方法是一個提取url中的參數的方法
實現過程

當你點擊toFirst會把url的msg參數變成toFirst對應的data-route,而後觸發showMsg方法,獲取到當前的url的參數在message中找到對應要渲染的字符,渲染在msg上。
效果以下
image

而後點擊toSecond

image

點擊瀏覽器的返回按鈕,經過監聽來從新調用showMsg方法,因此會回到這個頁面

image

若是你直接打開http://localhost:63342/history/index.html?msg=C
也會在index.html裏面進行showMsg處理來打開對應的頁面

image

完整代碼

<html>
<head>
    <title></title>
    <style type="text/css">
        div {
            margin: 10px;
        }
        .msgBtn {
            margin: 10px;
            padding: 10px;
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <div id="msg"></div>
    <br><br>
    <span class="msgBtn" data-route='A'>toFirst</span>
    <span class="msgBtn" data-route='B'>toSecond</span>
    <span class="msgBtn" data-route='C'>toThird</span>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script type="text/javascript">
        //路由註冊
        const message = {
            undefined: 'massage',
            A: 'hahahh',
            B: 'hehehehe',
            C: 'hohohoho'
        };
        //頁面分發加載
        function showMsg(el) {
            const _loc = location.href;
            let url_msg_id = GetRequest(_loc);
            el.html(message[url_msg_id.msg]);
        }
        //監聽前進後退按鈕
        window.addEventListener('popstate', function (e) {
            showMsg($('#msg'));
        });
        //點擊切換路由
        $('.msgBtn').click(function (e) {
            console.log(e.currentTarget );
            history.pushState(null, null, '?msg=' + e.target.dataset.route);
            showMsg($('#msg'));
        });
        //處理url
        function GetRequest() {
            let url = location.search; //獲取url中"?"符後的字串
            let theRequest = {};
            if (url.indexOf("?") !== -1) {
                let str = url.substr(1);
                let strs = str.split("&");
                for (let i = 0; i < strs.length; i++) {
                    theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
                }
            }
            return theRequest;
        }
        //頁面加載渲染
        showMsg($('#msg'));
    </script>
</body>

hash

實現思路和以前的大概相同就是變化後面的哈希值,而後經過hashchange監聽到哈希值的變化
demo地址 https://github.com/WindStormrage/Single-page-application/blob/master/hash/index.html

總結

這樣子是大概實現出了一個單頁面的效果可是,還有幾個單頁面的要素沒有體現出來,好比異步靜態文件的加載。

而後hash的缺點是,只好設置一層路由若是還有子頁面須要路由會比較麻煩,而後還有的是就是#這個字符讓url顯得很是不美觀

相關文章
相關標籤/搜索