以上幾篇的内容成功的将多頁面合成到單頁面上,然而還是有很多差別的, 多頁面切換的時候,通過浏覽器的自帶後退前進鍵可以進行導航,然而到目前為止,是沒有辦法進行的導航的,這一篇主要是引入了這個功能。
在進入正題之前,我們還需要對整體做一下調整, 一般情況下, 一個頁面分為兩個部分,一部分是靜态部分,隻要起到布局作用,還有一部分為改變部分,主要用來切換頁面的作用。在布局中設定一塊改動的區域,放置動态頁面, 不同的動态頁面調用靜态部分提供的方法來調整不同的布局。 這樣的結構如下圖所示。
界面的布局
這裡的做法就是讓靜态布局部分和動态部分都是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伺服器的搭建