单页应用
单页应用 (SPA)是一个提供了类似于桌面应用程序相似用户体验而全站只有一个页面的网站应用或者网站站点。在一个SPA中,所有需要的代码-HTML,JavaScript,和CSS-在单页面加载时取得,或者在需要的时候会动态加载合适的资源增加到页面中,这一般都是根据用户的操作来相应的。页面不会在过程中的任何时间重新加载,尽管location hash和HTML5 History API可以被用于给程序中的分开逻辑页面提供导航的能力但是他也不会把控制权转交给任何一个其他页面。单页应用通常会在应用背后动态的跟web服务器进行数据交换(数据传输)。
历史
单页应用短语的由来是不清楚的,这个概念早在2003年就被开始讨论。Stuart Morris在2004年4月的时候在slashdotslash.com上编写了独立页面来描述什么是单页应用,在同一年的稍晚些时候Lucas Birdeau, Kevin Hakman, Michael Peachey 和 Evan Yeh就在美国专利8,136,109描述了单页应用的应用.
JavaScript可以在web浏览器上使用展示用户UI,运行应用逻辑,然后和web服务器进行数据交换。成熟的开源库可以有效的为构建一个单页应用提供支持,开发人员也可以减少大量的JavaScript编写工作。
技术途径
有各种技术使浏览器即使是当应用需要和服务器进行通讯的时候也能够维持一个单页应用。
JavaScript框架
web浏览器框架类似 AngularJS, Ember.js, Meteor.js, ExtJS 和 React 都是兼容SPA原则的。
- AngularJS完全是一个客户端的框架。AngularJS的模板是基于双向数据绑定的。数据绑定是一个当model改变的时候自动更新视图的方式,当视图发生改变的时候他同样的也会更新model。HTML模板在浏览器中被编译。编译阶段创建纯净的HTML,浏览器会重新渲染这些HTML到现有的视图中。这个阶段会在后续的页面视图重复。在传统的服务器端HTML程序中,类似控制器和model的概念结合生产出新的HTML视图。在AngularJS框架中,控制器和model的状态在客户端浏览器中维护。因此,新页面可以在没有任何服务器参与的情况下被生成。
- Ember.js是一个客户端JavaScript web应用框架,他是基于model-视图-控制(MVC)软件架构模式的框架。他允许开发者通过可混合的风格和富对象,声明双向数据绑定,计算属性和Handlebars.js提供的自动更新模板能力,管理应用路由状态这些框架最佳实践模式来创建可伸缩的单页应用。
- Meteor.js是一个全栈(客户端到服务端)JavaScript框架他是专门为SPA设计的。他的数据绑定比Angular,Ember 或者 ReactJS更简单,他使用分布式数据协议(Distributed Data Protocol)和发布订阅模式来在不需要开发者写任何同步代码的情况下实时自动传播数据改变给客户端。全栈响应确保了所有层面,从数据库到模板,可以在需要的时候自动更新他们自己。Ecosystem系统类似于服务端渲染它解决了搜索引擎优化的问题。
- Aurelia是一个手机端,桌面端,浏览器端JavaScript框架。他和AngularJS相似,但是更新,更符合标准,采用了模块化的方式。Aurelia使用下一代ECMAScript书写。
- Vue.js 是一个开源JavaScript框架他用来构建用户界面。结合cli和webpack我们可以更简单的创建SPA。
Ajax
使用最多的技术现在是Ajax。占主导地位的是使用JavaScript中的XMLHttpRequest/ActiveX Object(不赞成)对象,其他Ajax方式包括IFRAME或者script HTML元素。像jQuery这类流行的库,他在不同浏览器中标准化Ajax的行为,这使Ajax更受欢迎。
Websockets
Websockets是客户端和服务端双向实时状态通讯技术是HTML规范的其中一部分。Ajax的升级版,比Ajax更高效更简单。
Server-sent events
服务器推送事件(SSEs)是一个借由以服务器开始传入数据到浏览器客户端的技术。一旦一个初始化连接被建立,一个事件流会保持打开直到浏览器关闭。SSEs通过传统HTTP发送,有各种特性可以弥补Websockets设计上的缺陷,分别是自动重新连接,事件ID,和任意传入事件的能力。
Browser plugins
尽管这个方法已经过期,异步调用服务同样可以使用类似Silverlight, Flash, 或者 Java applets这些浏览器插件来达到目的。
Data transport (XML, JSON and Ajax)
请求服务器通常是请求原始数据(XML或者JSON,等等),或者是返回新的HTML。当服务器返回HTML时,客户端的JavaScript会更新部分区域的DOM。当原始数据返回时,通常客户端的JavaScript会处理XML/(XSL)(还有JSON模板)用这些原始数据转换成为HTML,用来更新部分区域的DOM。
服务端架构
Thin server architecture
一个单页面应用面把逻辑从服务端转移到客户端。他的结果就是web服务端的角色进化成为一个纯净的数据API服务或者提供web服务。这个架构的转变,在一些圈子中被成为”瘦服务端架构”他强调把复杂从服务端转移到客户端,最终减低了系统的整体复杂度。
Thick stateful server architecture
服务器会在内存中保存需要的客户端页面的状态。通过这种方式,当任何请求到达服务端(通常是用户操作),服务端根据客户端具体的改变和希望达到的状态发送对应的HTML和/或者JavaScript(通常是增加/删除/更新一部分客户端的DOM)。同时,服务端的对应状态会更新。大多数的逻辑在服务端运行,通常HTML也会在服务端渲染。某种程度上来说,服务端充当一个web浏览器,接受事件和在服务端处理状态改变自动传播给客户端。这个方式需要更多的内存和服务器处理,但是优势是简单的开发模式因为 a)通常应用的大部分代码在客户端 b)数据和UI状态在服务端共享同一片内存空间这就不需要服务端和客户端建立通讯桥梁。
Thick stateless server architecture
这是富状态服务器的变种。客户端页面通常通过Ajax请求发送数据给服务端用来代表自身的状态。通过数据,服务端就可以重新构建客户端需要修改的部分页面的状态和生成必要的数据或者代码(JSON或者JavaScript),返回给客户端新的状态,通常是根据对应的请求和客户端操作修改页面的DOM。
这个方式传送更多的数据给服务器和每个请求需要更多的计算资源来在服务端部分或者全部重新构建客户端页面的状态。同时,这个方式可以更简单的升级因为在服务端没有每一个客户端的页面数据保存,因此,Ajax请求可以在不需要同域数据共享或者服务器关联的情况下可以发送给不同的服务器。
本地运行
一些单页应用程序也许会使用文件URL方案从一个本地文件被执行。这给了用户从一个在不依赖和服务器链接的情况下从服务器下载单页应用程序然后从本地存储中运行单页应用程序文件的能力。如果这类单页应用程序像保存和更新数据,这必须使用以浏览器为基础的Web存储。这些程序都是得益于HTML5的使用。
单页应用程序的挑战
因为单页应用程序是从无状态页面渲染模式进化而来的,而浏览器也是根据这个模式设计的,那么一些新的挑战也就出现了。这些问题每个都由一个有效的解决方案:
- 客户端JavaScript库处理各种问题。
- 针对单页应用模式专门的服务端web框架。
- 浏览器的进化和专门为单页应用模式设计的HTML5 API。
搜索引擎优化
因为一些流行的Web搜索引擎爬虫对JavaScript执行的缺乏,SEO(搜索引擎优化)有从公开web站点转变成单页应用模式而带来的历史遗留问题。
在2009到2015年之间,谷歌web管理中心提议和推荐一个”AJAX爬虫模式”在片段中使用一个惊叹号标志标识一个带状态的AJAX页面 (#!)。这个特指行为应用在单页应用程序上允许搜索引擎爬虫提取相关的原始数据。对于那些不支持URL hash的引擎,SPA的URL hash将被视而不见。这些”hash URL”被那些包括W3C中的Jeni Tennison的一些人认为是一个问题因为他让那些没有激活JavaScript的浏览器无法访问。他也打破了浏览器HTTP头部引用不允许在引用头部发送部分标识符的约束。在2015年,Google取消了Ajax hash爆炸的提议。
另外,应用需要在服务器渲染第一个页面然后随后的页面在客户端更新。这在传统上比较困难的,因为渲染代码可能需要在服务端和客户端用不同的语言或者框架书写。使用无逻辑的模板,跨语言的编译,或者在服务端或者客户端使用同样的语言获取可以使大量的代码得到共享。
因为SEO在SPA中不能很好兼容,值得注意的在需要搜索引擎抓取的页面通常是使用SAP的。使用场景包括隐藏在权限系统背后表面私有数据的应用程序。在应用程序电子商务的场景下,通常是经典的”页面重绘”模式是在应用的着陆页和营销网站,他们提供的原始数据作为搜索引擎搜索显示的点。博客,论坛和其他的传统页面手工重绘通常可以给搜索引擎发送相关的点。
其他的方式是使用服务器为中心的web框架比如基于Java的ItsNat他在服务端使用同一个语言和模板技术渲染任何一个超文本。在这个方式中,服务端可以精确的知道客户端Dom的状态,任何大或者小的页面更新都需要服务器生成,然后通过Ajax传输,Javascript代码会给客户端页面带来新的状态执行DOM方法。开发者可以决定哪个页面状态可以被web蜘蛛爬取作为SEO和在加载时根据状态生成纯HTML而不是Javascript。在ItsNat框架的场景下,这是自动的因为ItsNat在服务端保存客户端的DOM树作为一个Java W3C树;在加载时为Ajax请求在服务端渲染Dom树生成纯HTML和Javascript DOM操作。这对SEO是非常重要的因为开发者可以在服务端用相同的Java代码基于纯净的HTML的模板构建得到想要的DOM。在页面加载时间,转换HTML的生成是ItsNat根据DOM的状态做的SEO兼容。在1.3版本,ItsNat提供了一个新的无状态模式,客户端的DOM不在保存在服务端因为和无状态模式客户端,当客户端发送的任何Ajax请求数据通知当前DOM的状态这些DOM状态部分或者全部在服务端重新构建;无状态模式可能仍然是SEO兼容的因为SEO兼容性发生在初始页面的加载时间这不受有状态或者无状态模式的影响。
有几个解决方案让web站点看起来是可爬的。他们都涉及创建独立的HTML页面倒影SPA的内容。服务端可以创建基于HTML版本的站点然后传送他们使他们可爬,或者使用没有界面的浏览器类似PhantomJS来运行
JavaScript应用输出HTML结果。
这些都需要不少的努力,最终可以结束大型复杂站点的维护噩梦。他们也仍然有SEO潜在陷阱。如果服务端生成的HTML被认为和SPA内容不同,那么对站点将会不利。运行PhantomJS输出HTML可以减慢页面响应的速度,这会导致搜索引擎,特别是Google搜索引擎的排名降级。
客户端/服务器代码分离
使服务端和客户端的代码可以大量的共享的一种方式是使用无逻辑的模板语言类似Mustache或者Handlebars。这类模板可以从不同的宿主语言渲染,类似服务端使用Ruby客户端使用Javascript。然而,仅仅使用共享模板通常需要使用重复的业务逻辑用正确的模板和数据去填充他们。当只需要更新一小部分的页面时从模板渲染可能会给性能带来负面的影响-类似从一个很大的模板上更新一个input的文本。不是仅仅更新改变的位置,替换整个模板可能仍然会影响用户选择和鼠标位置。为了避免这些问题,应用可以使用UI数据绑定或者只更新页面合适的位置而不是重新渲染整个模板的方式。
浏览器历史记录
根据SPA的定义”单页面应用”,这个模式打破了浏览器为页面设计的前进/后退导航按钮。这表示当用户点击后退按钮时将会没用,单页面卸载之后浏览器历史记录了前面的记录,而不是SAP中前面屏幕的状态。
传统的解决方法是SPA需要修改浏览器URL的hash来标识当前屏幕的状态。这可以通过Javascript达到,然后使URL历史记录事件在浏览器中构建。一旦SPA有能力在同一个屏幕通过URL hash拯救,期望的后退按钮就回来了。
未来解决这个问题,HTML5专门介绍了pushState和replaceState提供了让程序读取真正的URL和浏览器历史记录。
分析
类似Google分析的分析工具大量依赖在浏览器中整个新页面的加载,根据新页面加载初始化。SPA不是这样工作的。
第一个页面加载之后,所有随后的页面和内容修改由应用内部处理,这应该简单的调用一个方法去更新分析工具。不能调用这个方法,浏览器不会触发新页面的加载,浏览器记录中也不会增加任何东西,分析工具将没办法知道谁在站点上干了什么。
SPA中增加页面加载
使用HTML5历史API在SPA中增加页面加载事件是可能的;这将帮助整合分析工具。难度主要来自于管理和确保全部东西都被正确跟踪-这牵涉了检查丢失报告和双入口。一些框架提供开源分析工具整合解决这些问题。开发者可以不必从头做起只需要整合他们到你的应用程序来确保一切都正确工作。
初始化加载的速度
单页面应用程序比基于服务器的应用程序第一个页面的加载速度要更慢。这是因为第一次加载需要在浏览器在渲染需要的view之前把框架和应用程序代码取下来。一个基于服务器的应用程序只需要把HTML发送给浏览器,减少延迟和下载时间。
增加页面加载速度
增加SPA初始化加载速度有几种办法,类似缓存方式和当需要的时候懒加载模块。但是不可避免的需要下载框架,至少是一部分的应用代码,然后将会在浏览器显示东西之前调用API请求数据。这是一个”现在就付钱,和等下付钱的”讨价还价的剧情。性能和等候时间的决定是开发者必须做出的。
页面生命周期
一个SPA是完全在初始加载页面阶段加载然后页面区域根据需求从服务端替换或者更新新的页面。为了避免下载未使用的特性,SPA将会逐渐下载他们需要的更多特性,不论是小部分的页面,还是用来组成屏幕上的模块。
这样就存在一个SPA中的”状态”和传统网站中的”页面”的比较,因为”状态导航”在类似于页面导航,理论上,任何基于页面的web站点可以通过替换统一页面转换为单页面程序只需要修改和比较非SPA的结果就可以。
在web上SPA方式类似于原始桌面程序流行的单文档接口(SDI)技术。