天天看點

原生開發移動web單頁面(step by step)6——history api應用

以上幾篇的内容成功的将多頁面合成到單頁面上,然而還是有很多差別的, 多頁面切換的時候,通過浏覽器的自帶後退前進鍵可以進行導航,然而到目前為止,是沒有辦法進行的導航的,這一篇主要是引入了這個功能。

在進入正題之前,我們還需要對整體做一下調整, 一般情況下, 一個頁面分為兩個部分,一部分是靜态部分,隻要起到布局作用,還有一部分為改變部分,主要用來切換頁面的作用。在布局中設定一塊改動的區域,放置動态頁面, 不同的動态頁面調用靜态部分提供的方法來調整不同的布局。 這樣的結構如下圖所示。

界面的布局

這裡的做法就是讓靜态布局部分和動态部分都是Page執行個體對象,通過App的執行個體對象初始化并渲染首頁,如以下代碼:

app.initialize(staticPage, indexPage);
           

切換頁面的代碼與之前的一樣, 介紹了這個後,接下來介紹history api。

浏覽器history記錄是類似一個數組, 通過一個索引值代表目前的頁面,如果按後退鍵,将會索引值減一, 跳到之前的頁面,按前進鍵則相反,如下圖所示。

history棧

浏覽器history api提供了兩個方法來操作這個曆史資料的數組: pushState和replaceState。 pushState方法的作用為在目前頁面所在的索引值插入一個新的記錄,然後移除所有後面的記錄,與使用者單擊一個連結大緻相同, 并将曆史的索引值轉到新的記錄。如下圖所示

pushState操作

replaceState方法的作用為替換目前的記錄,不影響曆史記錄數組中其它資料的更改,如下圖所示

replaceState操作

當點選浏覽器的前進後退鍵的時候,就會觸發popstate的事件, 通過監聽事件擷取到的曆史數組中狀态的改變而動态更改頁面,調用history的方法也會觸發popstate事件。

下面介紹了如何把它內建到App對象中,App構造函數的代碼如下

function App() {
    this.currentPage = null;
    this.staticPage = null;
    this.pageContainer = null;
    this.routeObj = {};
}
           

staticPage存放靜态布局頁面,可以調用其方法更改外部布局

pageContainer是可變頁面的容器

routeObj對象放置頁面對象,key代表頁面的url, value為Page對象

nitialize方法代碼如下

initialize: function (staticPage, indexPage) {
    staticPage = this.staticPage = staticPage || App.emptyPage;
    var that = this;

    staticPage.render(function (html) {
        var body = document.body;
        body.insertAdjacentHTML("afterbegin", html);
        staticPage._initialize(body);
        if (staticPage.domList.pageContainer) {
            that.pageContainer = staticPage.domList.pageContainer;
        }
        else {
            console.error("staticPage must have pageContainer");
        }
        that.render(indexPage, true);

        window.addEventListener("popstate", function (ev) {
            if (ev.state && ev.state.data) {
                var url = ev.state.data;
                var page = that.routeObj[url];
                that._renderPage(page);
            }
        }, false);
    });
},
           

這裡需要靜态布局頁面有個PageContainer,讓動态頁面放置。

_attachHistory方法的代碼如下

_attachHistory: function (page, isBack) {
    var newUrl =  page.url;
    
    if (isBack) {
        history.replaceState({data: newUrl}, "", newUrl);
    }
    else {
        history.pushState({data: newUrl}, "", newUrl);
    }
},
           

_renderPage方法的代碼如下

_renderPage: function (page) {
    if (this.currentPage) this.currentPage._dispose();
    this.currentPage = page;
    page.app = this;

    document.title = page.title;
    var pageContainer = this.pageContainer;
    page.render(function (html) {
        pageContainer.innerHTML = html;
        page._initialize(pageContainer);
    });
}
           

修改Page構造函數與App.createPage函數

Page構造函數如下

function Page(title, url) {
    this.title = title;
    this.url = url;
    this.domList = {};
    this.eventList = [];
}
           

App.createPage代碼如下

App.createPage = function (title, url, options) {
    var page = new Page(title, url);
    App.extend(page, options);
    return page;
}
           

然後對應建立一個App.emptyPage, 代碼如下

App.emptyPage = App.createPage("", "", {
    render: function (fn) {
        fn("<div class='pageContainer'></div>");
    },
    getDomObj: function (dom) {
        this.attachDom(".pageContainer", "pageContainer", dom);
    }
});
           

修改entry.js, login.js, register.js和goal.js,定義各自Page執行個體對象的時候,增加url的字段,

分别為/serve/entry, /serve/login, /serve/register和/serve/goal, 以serve開頭的,是為了頁面重新整理的時候,伺服器檢索到url以/serve開頭,然後跳轉到首頁。

更改提供背景服務頁面的serverFile.js修改exports.getFile方法,代碼如下

exports.getFile = function (req, res, key, root) {
    if (key.indexOf("/public/")  === 0) {
        serveFile(req, res, root + key, root);
    }
    else if (key.indexOf("/serve") === 0) {
        serveFile(req, res, root + "/public/serve/index.html", root);
    }
    else {
        res.statusCode = 404;
        res.end("404");
    }
}
           

修改index.js的代碼,如下

var app = new App();
app.initialize(null, entryPage);
           

如果想要重新整理跳到目前頁面,那就需要在類似在sessionStorage的存儲機制中存放目前的頁面url,然後在初始化的時候,将entryPage替換為該url的page對象即可。

總結: 加入history api後,頁面表現和普通的多頁面相差無幾了,可以跳轉, 可以後退。 改變頁面後title和url也随着改變,重新整理直接跳到首頁也可以跳到目前頁。到了這篇為止,單頁面改造完畢,然而,單頁面的靈活性遠非多頁面可比的, 如果處理得到,複雜性卻不會增加很多。 接下來的篇章就是為了讓單頁面更加出彩, 甚至達到原生app的水準。

後續更新:下一篇将重點介紹css3的animation, 配合頁面切換, 讓頁面切換動态化。css3的animation子產品在後面的部分使用的非常頻繁,為了解後面的代碼做一下基礎。

案例連結 原生開發移動web單頁面(step by step)1——傳統頁面的開發 原生開發移動web單頁面(step by step)2——Page對象 原生開發移動web單頁面(step by step)3——App對象 原生開發移動web單頁面(step by step)4——tap事件與slide事件 原生開發移動web單頁面(step by step)5——nodejs伺服器的搭建

繼續閱讀