一、简介
SPA即单页面应用(Single Page Application),说简单一点就是把所有操作放在一个页面里,通过JS去实现相关操作,目的之一是为了减轻服务器的压力。关于SPA的详细介绍可看百度百科给出的介绍。 —— [ 百度百科 ]
二、优缺点
-
1. 优势
-
1)首先,最大的好处是用户体验,对于内容的改动不需要加载整个页面。这样做好处颇多,因为数据层和UI的分离,可以重新编写一个原生的移动设备应用程序而不用(对原有数据服务部分)大动干戈;
2)单页面Web应用层程序最根本的优点是高效。它对服务器压力很小,消耗更少的带宽,能够与面向服务的架构更好地结合;
**2. 缺点 **
-
1)对技术要求比较高,开发难度大
2)不利于SEO
3)由于单页是基于JS操作的,因此对浏览器的兼容性比较麻烦(其中大部分问题已基于HTML5有所改进)
三、实现方式
实现方式有很多,移动端可以使用 vue.js 框架,PC端有使用 angularjs 实现的,我这里使用最原始的 jQuery 实现(主要是我不会 angularjs 哈哈)。
这里准备了5个html页面用于示例说明,分别为:
主页面(一个)
子页面(三个)
子子页面(一个)
主页面
index.html
<script type="text/javascript" src="js/jquery-2.2.3.min.js"></script>
<script type="text/javascript" src="js/index.js" ></script>
........
<div style="float:left;border:1px solid red;margin:20px">
<p>
<a href="javascript: void(0);" id="menu1" data-type="CRM" data-url="page1.html">用户管理</a>
</p>
<p>
<a href="javascript: void(0);" id="menu2" data-type="CRM" data-url="page2.html">角色管理</a>
</p>
<p>
<a href="javascript: void(0);" id="menu3" data-type="CRM" data-url="page3.html">权限管理</a>
</p>
</div>
<div style="float:left;border:1px solid blue;margin:20px" id="main">
<div style="float:left;border:1px solid red;margin:20px" id="content">
</div>
</div>
index.js
var params = {
"id": "123",
"name": "张三"
}
var cachePage = new Array();
$(function() {
//添加链接的处理事件
$("a[data-type='CRM']").click(ajax);
//添加popstate事件
$(window).on("popstate", function() {
var presentName = history.state && history.state.presentName;
if(presentName) {
console.log(presentName);
$("#main").html('');
var isLast = true;
$.each(cachePage, function(k, v) {
if(v.presentName == presentName) {
$("#main").append(v.presentPage);
isLast = false;
return false;
}
});
console.log(isLast + "++++++++++");
}
});
});
/**
1. 核心
2. @param {Object} event
3. @param {Object} isPopstate
*/
function ajax(event, isPopstate) {
var url = $(this).attr('data-url');
var browserPath = location.href.split("?");
// 修改当前状态信息(修改历史记录)
// 即将即将替换的页面信息更新到集合中
if(browserPath.length > 1) {
var urlParams = browserPath[1].split('=');
if(urlParams.length > 1 && urlParams[0] == 'name') {
// 获取当前状态下的 presentName
var presentName = history.state.presentName;
console.log(presentName + "----");
var lastHtml = $("#content").clone(true);
var lastPage = {
'presentName': presentName,
'presentPage': lastHtml
};
for(var i = 0, flag = true, len = cachePage.length; i < len; flag ? i++ : i) {
if(cachePage[i] && cachePage[i].presentName == presentName) {
cachePage.splice(i, 1);
flag = false;
} else {
flag = true;
}
}
cachePage.push(lastPage);
}
}
$.ajax({
type: "get",
url: url,
async: false,
success: function(data) {
$("#content").html(data);
}
});
var title = this.id;
document.title = title;
// 保存当前记录【注:该记录只保存了页面初次信息,后续页面所做操作信息无法保存】
if(!isPopstate) {
var presentName = title + "_" + new Date().getTime();
console.log("哈哈:" + presentName + "----");
var presentHtml = $("#content").clone(true);
var presentPage = {
'presentName': presentName,
'presentPage': presentHtml
};
console.log("集合大小:" + cachePage.length);
cachePage.push(presentPage);
console.log("集合长度:" + cachePage.length);
history.pushState({
presentName: presentName // 当前
}, "", location.href.split("?")[0] + "?name=" + presentName);
}
}
子页面
- page1.html
<body>
<h1 style="color: red;">第一个页面</h1>
<input type="text" name="text01" id="text01" class="text-info" value=""/>
<input type="button" class="btn-success" onclick="test()" value="测试"/>
<div id="test1"></div>
</body>
<script type="text/javascript">
function test() {
alert(params.id + "----" + params.name);
}
$(function(){
var array = new Array();
array.push({'url': 'page1.1.html', 'text': '员工管理~'});
var ahtmls = initChildA(array);
$('#test1').append(ahtmls);
$("#test1 a[data-type='CRM']").click(ajax);
});
function initChildA(params) {
var ahtmls = '';
var idPrefix = '';
var query = location.href.split("?")[1];
var idStr = query.split("=")[1];
if (idStr.split('-').length > 0) {
idPrefix = idStr.split('-')[0];
} else {
idPrefix = idStr;
}
$.each(params, function(k, v) {
ahtmls += '<a href="javascript: void(0);" id="' + idPrefix + '-' + (k + 1) + '"';
ahtmls += ' data-type="CRM", data-url="' + v.url + '">' + v.text + '</a>';
});
return ahtmls;
}
</script>
- page2.html
<body>
<h1 style="color: blue;">第二个页面</h1>
<h1 style="color: blue;">第二个页面</h1>
<input type="button" class="btn-info" value="返回" onclick="goBack()"/>
</body>
<script type="text/javascript">
function goBack() {
window.history.go(-1);
}
</script>
- page3.html
<body>
<h1 style="color: blueviolet;">第三个页面</h1>
<h1 style="color: blueviolet;">第三个页面</h1>
<h1 style="color: blueviolet;">第三个页面</h1>
<a href="javascript: void(0);" class="btn-link" onclick="javascript :history.back(-1);">返回上一页</a>
</body>
子子页面
page1.1.html
<body>
<h1 style="color:chartreuse;">第一个页面的子页面</h1>
<h1 style="color:chartreuse;">第一个页面的子页面</h1>
<h1 style="color:chartreuse;">第一个页面的子页面</h1>
<a href="javascript: void(0);" class="btn-link" onclick="javascript :history.back(-1);">返回上一页</a>
</body>
到此为止基本上是实现了单页面操作,这个例子基本实现后退时保留历史操作记录(不刷新页面)。
上述示例未解决的问题是最后点击的那个页面,无法记录下操作后页面的数据
在测试过程中,发现了一个很有意思的事情,操作流程如下:
- 依次点击:page1 --> page2
- 返回(按钮/浏览器均可)page1
- 再点击 page3,然后连续返回
- 预想结果为:page3 --> page1 –> page2 --> page1 --> index
- 而实际结果为:page3 --> page1 --> index
为此我还特意做了一个多页面的例子,发现也是一样的效果,后来我猜想是因为浏览器在返回page1后再点击,浏览器会更新之前page1对应的那条历史记录。
有谁知道具体原因,欢迎在下面评论告诉我
使用JQ来实现单页面应用还是比较费劲,推荐使用Vue来实现。