原生开发移动web单页面(step by step)6——his
以上几篇的内容成功的将多页面合成到单页面上,然而还是有很多区别的, 多页面切换的时候,通过浏览器的自带后退前进键可以进行导航,然而到目前为止,是没有办法进行的导航的,这一篇主要是引入了这个功能。
在进入正题之前,我们还需要对整体做一下调整, 一般情况下, 一个页面分为两个部分,一部分是静态部分,只要起到布局作用,还有一部分为改变部分,主要用来切换页面的作用。在布局中设置一块改动的区域,放置动态页面, 不同的动态页面调用静态部分提供的方法来调整不同的布局。 这样的结构如下图所示。
界面的布局这里的做法就是让静态布局部分和动态部分都是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服务器的搭建