一种SPA单页面应用架构.docx
《一种SPA单页面应用架构.docx》由会员分享,可在线阅读,更多相关《一种SPA单页面应用架构.docx(7页珍藏版)》请在冰豆网上搜索。
一种SPA单页面应用架构
种SPA(单页面应用)架构
如果对SPA概念不清楚的同学可以先自行了解相关
概念)平时喜欢做点小页面来玩玩,并且一直采用单页面应用
SinglePageApplication)的方式来进行开发。
这种开发方式是在之前一年做的一个创业项目的经验和思考,一直想写篇博客来总结一下。
个人认为单页面应用的优势相当明显:
前后端职责分离,架构清晰:
前端进行交互逻辑,后端负责数据处理。
前后端单独开发、单独测试。
良好的交互体验,前端进行的是局部渲染。
避免了不必要的跳转和重复渲染。
当然,SPA也有它自身的缺点,例如不利于搜索引擎优化等等,这些问题也有其相应的解决方案。
面要介绍的这种方式可以说是一种模式或者工作流,和前不能说是TheBestPractice,我相信经过更多人的讨论和思考会有ABetterPractice。
:
)
概览
图展示了这种模式的整个前后端及各自的主要组成:
看起来有点复杂,接下来会仔细地对上面每一个部分进行解释。
看完本文,就应该能理解上图中的各部件之间的交互流程。
前端架构把上图的前端部分单独抽出来进行研究:
前端中大致分为四种类型的模块:
components:
前端UI组件
services:
前端数据缓存和操作层
databus:
封装一系列Ajax操作,和后端进行数据交互的部
common/utils:
以上组件的共用部件,可复用的函数、数据
components
个博客的评论功能:
我们可以把博客评论做为一个组件,这个组件有自己的结构
(html),外观(css),交互逻辑(js),所以我们可以单独做一个叫comment的component,由以下文件组成:
comment.htmlcomment.csscomment.js
每个component可以想象成一个工程,甚至可以有自己的
README、测试等)
componentstree
个component可以依赖另外一个component,这时候它们是父子关系;component之间也可以互相组合,它们就是兄弟关系。
最后的结果就类似DOMtree,component可以组成componentstree。
例如,现在要给这个博客添加两个功能:
显示评论回复。
鼠标放到评论或者回复的用户头像上可以显示用户名片。
我们构建两个组件,reply和user-info-card。
因为每个
comment都要有自己的回复列表,所以comment组件是依赖于reply组件的,comment和reply组件是嵌套关系。
而user-info-card可以出现在comment或者reply当中,并且为了以后让user-info-card复用性更强,它应该不属于任何一个组件,它和其他组件是组合关系。
所以我们就得到个简单的componenetstree:
components之间的通信怎么可以做到鼠标放到评论和回复的用户头像上显示名片呢?
这其实牵涉到组件之间是如何进行通信的问题。
最佳的方式就是使用事件机制,所有组件之间可以通过一个叫eventbus通用组件进行信息的交互。
所以,要做到上述功能:
user-info-card可以在eventbus监听一个
user-info-card:
show的事件。
而当鼠标放到comment和reply组件的头像上的时候,组件可以使用eventbus触发user-info-card:
show事件。
user-info-card:
//
vareventbus=require("eventbus")eventbus.on("user-info-card:
show",function(user){
显示用户名片})commentorreply:
vareventbus=require("eventbus")$avatar.on("mouseover",function(event){
eventbus.emit("user-info-card:
show",userData)
})components之间用事件进行通信的优势在于:
组件之间没有强的依赖,组件之间被解耦。
组件之间可以单独开发、单独测试。
数据和事件都可以简单的进行伪造进行测试(mocking)。
总结:
component之间有嵌套和组合的关系,构成
componentstree;component之间通过事件进行信息、数据的交换。
servicescomponent的渲染和显示依赖于数据(model)。
例如上面的评论,就会有一个评论列表的model。
comments:
[
{user:
..,content:
..,createTime:
..},{user:
..,content:
..,createTime:
..},
{user:
..,content:
..,createTime:
..}
每个评论的component会对应一个comment(comments
数组中的对象)进行渲染,渲染完以后就会正确地显示在页
面上。
commentcomponent不会自己直接保存这些commentmodel。
这些model都会保存在service当中,而component
会从service拿取数据。
components和services之间是多对多的关系:
一个component可能会从不同的services中拿取数据,而一个service可能为多个components提供数据。
services除了用于缓存数据以外,还提供一系列对数据的些操作接口。
可以提供给components进行操作。
这样的好处在于保持了数据的一直性,假如你使用的是MVVM框架
进行component的开发,对数据的操作还可以直接对多个视
components的视图也会相应地得到更新。
总结:
services是对前端数据(也就是model)的缓存和操作。
databus
拉去数据。
而这里建议不直接这样做,而是把各种和后端的
API进行交互的接口封装到一个叫databus的模块当中,这里的databus相当于是“对后端数据进行原子操作的集合”。
如上面的commentservice需要从后端进行拉取数据,它会这样做:
调用
vardatabus=require("databus")varcomments=nulldatabus.getAllComments(function(cmts){//databus方法进行数据拉取
comments=cmts
})
databus.getAllCommetns=function(callback){
utils.ajax({
url:
"/comments",method:
"GET",success:
callback
})
这样做是因为,不同的services之间可能会用到同样的接口
对后端进行操作,把操作封装起来可以提高接口的复用性。
这操作也可以被components所调用(例如退出、登录等)总结:
databus封装了提供给services和component和后端
API进行交互的接口。
common/utils
这两个模块都可以被其他组件所依赖。
common,故名思议,组件之间的共用数据和一些程序参数可以缓存在这里。
utils,封装了一些可复用的函数,例如ajax等。
eventbus
所有组件(特别是components之间)的通过事件机制进行数据、消息通信的接口。
可以简单地使用EventEmitter这个库来实现。
后端架构传统的网页页面一般都是由后端进行页面的渲染,而在我们的架构当中,后端只渲染一个页面,其后,后端只是相当于
个WebService,前端使用Ajax调用其接口进行数据的调
取和操作,使用数据进行页面的渲染。
这样的好处就是,后端不仅仅能处理Web端的页面的请求,而且处理提供移动端、桌面端的请求或者作为第三方开放接口来使用。
大大提高后端处理请求的灵活性。
后端对比起前端的架构来说会简单很多,但是这只是其中种模式,对于不同复杂程度的应用可能会做相应的调整。
后
端大概分为三层:
CGI:
设置不同的路由规则,接受前端来的请求,处理数据,返回结果。
business:
这一层封装了对数据库的一些操作,business可以被CGI所调用。
database:
数据库,进行数据的持久化。
例如上面的comments的例子,CGI可以接收到前端发送的请求:
varcommentsBusiness=
//此处调用
require("./businesses/comments")app.get("/comments",function(req,res){comments的business数据库操作
commentsBusiness.getAllComments(function(comments)
//返回数据结果
res.json(comments)
})
后端的
})
API可以采用更规范的RESTfulAPI的方式,而
RESTful不在本文的讨论范围内。
有兴趣的可以参考Best
PracticesforDesigningaPragmaticRESTfulAPI
前后端的架构都基本清晰了,我们来看看文章开头的图:
看着图来,我们总结一下整个前后端的交互流程:
前端向服务端请求第一个页面,后端渲染返回。
前端加载各个component,components从services拿数据,
services通过databus发送Ajax请求向后端取数据。
后端的CGI接收到前端databus发送过来的请求,处理数据,调用business操作数据库,返回结果。
前端接收到后端返回的结果,把数据缓存到service,
component拿到数据进行前端组件的渲染、显示。
工作流
个好的工作流可以让开发事半功倍。
上面的这种单页面应用也有其相应的一种开发工作流,当然这种工作流也适合非单页面应用:
进行产品功能、原型设计。
后端数据库设计。
根据产品确定前后端的API(orRESTfulAPI),以文档方式
纪录。
前后端就可以针对API文档同时进行开发。
前后端最后进行连接测试。
前后端分离开发。
建议都可以采用
TDD(测试驱动开发)的
方式来单独测试、单独开发(关于
WebAPP测试这一块可
以单独进行讨论研究),提高产品的可靠性、稳定性。
完)