如何快速开发SPA应用
前言
web早已经进入了2.0时代了,如今的网页大有往系统应用级别的方向发展的趋势,再也不是以前的简单展示信息的界面了。如今很多webapp已经做到了原生应用的功能,并且运用自身的优势逐步取代之。HTML5也很给力,对多平台,多屏幕设备的良好兼容性使得前端工程师们在各种平台上大显身手。卤煮两年前进公司接到的也是一个SPA应用的项目,也颇有些自己的心得,今日就写篇博文,与大家分享下。
SPA
单页 Web 应用 (single-page application 简称为 SPA) 是一种特殊的 Web 应用。它将所有的活动局限于一个Web页面中,仅在该Web页面初始化时加载相应的HTML、JavaScript 和 CSS。一旦页面加载完成了,SPA不会因为用户的操作而进行页面的重新加载或跳转。取而代之的是利用 JavaScript 动态的变换HTML的内容,从而实现UI与用户的交互。由于避免了页面的重新加载,SPA 可以提供较为流畅的用户体验。得益于ajax,我们可以实现无跳转刷新,又多亏了浏览器的histroy机制,我们用hash的变化从而可以实现推动界面变化。从而模拟元素客户端的单页面切换效果:
优缺点
SPA被人追捧是有道理的,但是它也有不足之处。当然任何东西都有两面性,以下是卤煮总结的一些目前SPA的优缺点:
优点:
1.无刷新界面,给用户体验原生的应用感觉
2.节省原生(android和ios)app开发成本
3.提高发布效率,无需每次安装更新包。这个对于ios开发人员来说印象尤其深吧。
4.容易借助其他知名平台更有利于营销和推广
5.符合web2.0的趋势
缺点:
1.效果和性能确实和原生的有较大差距
2.各个浏览器的版本兼容性不一样
3.业务随着代码量增加而增加,不利于首屏优化
4.某些平台对hash有偏见,有些甚至不支持pushstate。(你懂的)
5.不利于搜索引擎抓取
框架
一种开发模式火起来之后,对应的框架会随之而起。像近几年比较火的angularJS,是目前中最流行的mvvm框架,非常适合做SPA;与之类似的还有vueJS,卤煮在项目中也用过,相对于前者比较轻量。还有早一些的backbone,提供最基本的mvc模式,其他的模块大小和细节得自己决定。最早接触的应该是extjs吧,这头超级巨无霸在很早的时候被用来创建企业后台应用,如今也跟着时代的变化做出了很多的改进。等等这些框架也好,库也好,都旨在为了更好的构建SPA应用而生的,它们优缺点卤煮就不在此一一提了。
hash值
在此处提到一个比较重要的概念:URL中的井号。其实它只是浏览地址中的一个特殊符号。在以前,我们经常用它来定位文档位置。例如以下代码:
<a href="target">go target</a>
......
<div id="target">i am target place</div>
点击a链接,文档会滚动到id为target的div的可视区域上面去。hash除了这个功能还有另一一种含义:指导浏览器的行为但不上传到服务器。大家都知道,改变url中的任何一个字符都会导致浏览器重新请求服务器,除了#号后面那段字符之外。所以,简而言之我们可以这样理解:改变#后面的值不触发网页重载,但会记录到浏览器history中去。
实现原理
实现SPA的方法有很多,终归一种遵循一种原则,界面无刷新。如果要实现原生应用中类似许多不同页面切换的效果,我们采用的是div切换显示和隐藏。而驱动div显示隐藏的方式有很多种
1.监听地址栏中hash变化驱动界面变化
2.用pushsate记录浏览器的历史,驱动界面发送变化
3.直接在界面用普通事件驱动界面变化
前两种方式较为普遍,因为它们的变化记录浏览器会保存在history中,可以通过回退/前进按钮找回,或者history对象中的方法控制。最后一种方法是用普通事件驱动的,没有改变浏览器的history对象,所以一旦用户按了返回按钮将会退到浏览器的主界面。所以,一般采用前两种方式。值得一提的是,在不支持hash监听和pushsate变化的浏览器中可以考虑用延时函数,不停得去监听浏览器地址栏中url发生的变化,从而驱动界面变化。
从零开始
卤煮在很久之前的一篇博文用pushstate的变化做了一个小小的示例,大家可以在之前的博文中找到它。在这里,我们用监听hash变化的方式展示SPA是怎么样运行工作的,同时从零开始,搭建一个基础的SPA。帮助大家理解简单的单界面应用的原理。
首先,我们画出三个div,它们实际上是作为三个界面存在界面上的,body作为界面外框容器,限制着它们的大小。为了给每个界面配对一个hash地址,我们给每个div配一个id,讲hash地址与对应的选择器(id、class)建立链接关系,从而可以从hash变化值中操作界面。
<body>
<div id="A" class="a J-A">A</div>
<div id="B" class="b J-B">B</div>
<div id="C" class="c J-C">C</div>
</body>
接下来,为它们添加样式,每个div都是全屏的,一开始只有A界面显示,其他的都隐藏之:
body {
height: 500px;
width: 100%;
margin: 0;
padding: 0;
}
div {
width: 100%;
height: 100%;
position: absolute;
font-size: 500px;
text-align: center;
display: none;
}
.a {
background-color: pink;
display: block;
}
.b {
background-color: red;
}
.c {
background-color: gray;
}
现在我们给网页添加上行为,首先需要知道的一点是,hash指即地址栏中#号后面的字符串,它的改变不会引起界面的刷新,但是会出发onhashchange事件,我们要做的就是监听这个事件:
function hashChanged(hashObj) {
//变化之后的url
var newhash = hashObj.newURL.split('#')[1];
//变化之前的url
var oldhash = hashObj.oldURL.split('#')[1];
//将对应的hash下界面显示和隐藏
document.getElementById(oldhash).style.display = 'none';
document.getElementById(newhash).style.display = 'block';
}
//监听路由变化
window.onhashchange = hashChanged;
目前,只需要以上的代码,我们便可以完成一个最简单的SPA,通过地址栏的变化,界面会相应地变化。当然,除了手动在地址栏里面改变hash的变化,我们也可以用代码改变它的变化,从而推动界面变化,下面是两种方式的效果图:
可以看到,左边是在浏览器中直接修改hash引起了界面的变化,而右边则是通过代码控制界面变化。这两种方式都可以在history中留下痕迹,从而当我们店家回退/前进按钮的时候追溯到之前的界面去。有些平台不允许我们去手动修改地址栏,(比如微信),那么我们一般采用第二种方式即可。况且,比较少的用户会去修改地址栏。
下面贴出所有的代码:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
body {
height: 500px;
width: 100%;
margin: 0;
padding: 0;
}
div {
width: 100%;
height: 100%;
position: absolute;
font-size: 500px;
text-align: center;
display: none;
}
.a {
background-color: pink;
display: block;
}
.b {
background-color: red;
}
.c {
background-color: gray;
}
</style>
</head>
<body>
<div id="A" class="a J-A">A</div>
<div id="B" class="b J-B">B</div>
<div id="C" class="c J-C">C</div>
</body>
<script type="text/javascript">
function hashChanged(hashObj) {
//变化之后的url
var newhash = hashObj.newURL.split('#')[1];
//变化之前的url
var oldhash = hashObj.oldURL.split('#')[1];
//将对应的hash下界面显示和隐藏
document.getElementById(oldhash).style.display = 'none';
document.getElementById(newhash).style.display = 'block';
}
//监听路由变化
window.onhashchange = hashChanged;
</script>
</html>
SEO优化
由于我们在处理单页应用的时候页面是不刷新的,所以会导致我们的网页记录和内容很难被搜索引擎抓取到。搜索引擎抓取页面首先要遵循http协议,可是#不是协议内的内容。而实际上也是这样,我们没有见过搜索引擎的搜索结果中,哪一条记录可以快速定位到网页内的某个位置的。解决的方法是用 #!号代替#号,因为谷歌会抓取带有#!的URL。(Google规定,如果你希望Ajax生成的内容被浏览引擎读取,那么URL中可以使用"#!"(这种URL在一般页面一般不会产生定位效果)),这样我们可以解决ajax的不被搜索引擎抓取的问题。在vueJs里面,我们可以看到作者就是这样做的。
结束
以上就是利用hash原理实现的一个很简单的SPA。当然,要实现项目中的单页应用,还有很多工作要做。比如传参,动画,异步资源加载的问题都是需要解决的。卤煮此处只是示范了一个很简单的例子,希望在做不复杂又不想引入其他框架的同学提供一点思路。另外单页虽好,但不要乱用哦,根据项目的具体需求制定对应的解决方案而不是一味的追潮逐流。
参考资料
搜索引擎会不会抓取带#号(哈希值)的URL
基于MVC的JavaScript Web富应用开发
如何快速开发SPA应用的更多相关文章
- 快速构建SPA框架SalutJS--项目工程目录 一
起因 刚进公司那会儿,接的是一个微信APP应用,SPA是前人搭起来的,用到的技术主要是backbone和zepto.后来那人走了,就卤煮一个人把项目接了下来.项目越是到后面,越发觉了诸多弊端,不停的增 ...
- Java快速开发平台强大的代码生成器,JEECG 3.7.5 VUE+ElementUI SPA单页面应用版本发布
JEECG 3.7.5 VUE+ElementUI SPA单页面应用版本发布 此版本为Vue+ElementUI SPA单页面应用版本,提供新一代风格代码生成器模板,采用Vue技术,提供两套精美模板E ...
- Java快速开发平台,JEECG 3.7.6性能增强版本发布
JEECG 3.7.6 性能增强版本发布 导读 ⊙Vue SPA单页面应用 ⊙Datagrid标签实现不同风格切换,支持BootstrapTable.EasyUI ⊙灵活通用代码生成器工厂 ...
- arcpy+PyQt+py2exe快速开发桌面端ArcGIS应用程序
前段时间有一个项目,大体是要做一个GIS数据处理工具. 一般的方法是基于ArcObjects来进行开发,因为我对ArcObjects不太熟悉,所以就思考有没有其他简单快速的方法来做. 在查看ArcGI ...
- 快速开发Grunt插件----压缩js模板
前言 Grunt是一款前端构建工具,帮助我们自动化搭建前端工程.它可以实现自动对js.css.html文件的合并.压缩等一些列操作.Grunt有很多插件,每一款插件实现某个功能,你可以通过npm命名去 ...
- winform快速开发平台->让有限的资源创造无限的价值!
最近一直在维护一套自己的快速开发平台. 主要应对针对C/S架构下的项目.然而对winform这快,还真没有看到过相对好的快速开发平台, 何为快速,在博客园逛了了好久, 预览了很多通用权限管理系统. 确 ...
- winform快速开发平台 -> 通用权限管理之动态菜单
这几个月一直忙APP的项目,没来得及更新项目,想想该抽出时间整理一下开发思路,跟大家分享,同时也希望得到宝贵的建议. 先说一下我们的权限管理的的设计思路,首先一个企业信息化管理系统一定会用到权限管理, ...
- winform快速开发平台 -> 工作流组件(仿GooFlow)
对于web方向的工作流,一直在用gooflow对于目前我的winform开发平台却没有较好的工作流组件. 针对目前的项目经验告诉我们.一个工作流控件是很必要的. 当然在winform方面的工作流第三 ...
- winform快速开发平台 -> 快速绑定ComboBox数据控件
通常我们在处理编辑窗体时.往往会遇到数据绑定.例如combobox控件绑定数据字典可能是我们经常用到的.然而在我的winform快速开发平台中我是如何处理这个频繁的操作呢? 首先,我们要绑定combo ...
随机推荐
- webp图片实践之路
最近,我们在项目中实践了webp图片,并且抽离出了工具模块,整合到了项目的基础模板中.传闻IOS10也将要支持webp,那么使用webp带来的性能提升将更加明显.估计在不久的将来,webp会成为标配. ...
- 从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点)
从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...
- 再讲IQueryable<T>,揭开表达式树的神秘面纱
接上篇<先说IEnumerable,我们每天用的foreach你真的懂它吗?> 最近园子里定制自己的orm那是一个风生水起,感觉不整个自己的orm都不好意思继续混博客园了(开个玩笑).那么 ...
- jQuery动画-圣诞节礼物
▓▓▓▓▓▓ 大致介绍 下午看到了一个送圣诞礼物的小动画,正好要快到圣诞节了,就动手模仿并改进了一些小问题 原地址:花式轮播----圣诞礼物传送 思路:动画中一共有五个礼物,他们平均分布在屏幕中,设置 ...
- 【原创分享·微信支付】C# MVC 微信支付之微信模板消息推送
微信支付之微信模板消息推送 今天我要跟大家分享的是“模板消息”的推送,这玩意呢,你说用途嘛,那还是真真的牛逼呐.原因在哪?就是因为它是依赖微信生存的呀,所以他能不 ...
- Xamarin+Prism开发详解三:Visual studio 2017 RC初体验
Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...
- PHP之GD函数的使用
本文讲解常用GD函数的应用 1.一个简单的图像 我们先看一个例子: <?php $w = 200; $h = 200; $img = imagecreatetruecolor($w,$h); $ ...
- 基于window7+caffe实现图像艺术风格转换style-transfer
这个是在去年微博里面非常流行的,在git_hub上的代码是https://github.com/fzliu/style-transfer 比如这是梵高的画 这是你自己的照片 然后你想生成这样 怎么实现 ...
- redis成长之路——(二)
redis操作封装 针对这些常用结构,StackExchange.Redis已经做了一些封装,不过在实际应用场景中还必须添加一些功能,例如重试等 所以对一些常功能做了一些自行封装SERedisOper ...
- Android中点击事件的实现方式
在之前博文中多次使用了点击事件的处理实现,有朋友就问了,发现了很多按钮的点击实现,但有很多博文中使用的实现方式有都不一样,到底是怎么回事.今天我们就汇总一下点击事件的实现方式. 点击事件的实现大致分为 ...