单页应用程序
单页应用程序
历史编辑
技术方法编辑
可以使用各种技术使浏览器即使在应用程序需要服务器通信时也能保留单个页面。
文档散列编辑
HTML作者可以利用元素ID来显示或隐藏HTML文档的不同部分。然后,使用CSS,作者可以使用“#target”选择器仅显示浏览器导航到的页面部分。
JavaScript框架编辑
Web浏览器JavaScript框架和库,如AngularJS、Ember.js、ExtJS、Knockout.js、Meteor.js、React、Vue.js和Svelte,都采用了SPA原则。除了ExtJS,所有这些都是免费的。
- AngularJS是一个完全客户端的框架。AngularJS的模板基于双向UI数据绑定。数据绑定是一种自动更新模型更改视图的方法,以及每当视图更改时更新模型的方法。HTML模板在浏览器中编译。编译步骤创建纯HTML,浏览器将其重新呈现到实时视图中。对于后续页面浏览,将重复此步骤。在传统的服务器端HTML编程中,控制器和模型等概念在服务器进程中交互,以生成新的HTML视图。在AngularJS框架中,控制器和模型状态在客户端浏览器中保持。因此,无需与服务器进行任何交互即可生成新页面。
- Ember.js是基于模型-视图控制器(MVC)软件架构模式的客户端JavaScript Web应用程序框架。它允许开发人员通过将常见习语和最佳做法集成到一个框架中来创建可扩展的单页应用程序,该框架提供丰富的对象模型、声明性双向数据绑定、计算属性、由Handlebars.js支持的自动更新模板以及用于管理应用程序状态的路由器。
- ExtJS也是一个客户端框架,允许创建MVC应用程序。它有自己的事件系统、窗口和布局管理、状态管理(商店)和各种UI组件(网格、对话框窗口、表单元素等)。它有自己的类系统,有动态或静态加载程序。使用ExtJS构建的应用程序可以单独存在(在浏览器中具有状态),也可以与服务器(例如,使用用于填充内部存储的REST API)一起存在。ExtJS仅内置使用localStorage的功能,因此大型应用程序需要服务器来存储状态。
- Knockout.js是一个客户端框架,使用基于Model-View-ViewModel模式的模板。
- Meteor.js是一个专为SPA设计的全栈(客户端服务器)JavaScript框架。它具有比Angular、Ember或ReactJS[5]更简单的数据绑定,[5]并使用分布式数据协议[6]和发布订阅模式自动向客户端传播数据更改,而无需开发人员编写任何同步代码。全堆栈反应性确保从数据库到模板的所有层在必要时自动更新自己。服务器端渲染[7]等生态系统软件包解决了搜索引擎优化的问题。
- React是一个用于构建用户界面的JavaScript库。它由Facebook、Instagram以及个人开发人员和公司社区维护。React使用一种新语言,即JS和HTML(HTML的一个子集)的混合。几家公司使用React with Redux(JavaScript库),这增加了状态管理功能,(与其他几个库一起)允许开发人员创建复杂的应用程序。[8]
- Vue.js是一个用于构建用户界面的JavaScript框架。Vue开发人员还为状态管理提供Vuex。
- Svelte是一个用于构建用户界面的框架,该框架将Svelte代码编译为JavaScript DOM操作,无需将框架捆绑到客户端,并允许更简单的应用程序开发语法。
阿贾克斯编辑
截至2006年,使用的最突出的技术是Ajax。[1] Ajax涉及对服务器的XML或JSON数据使用异步请求,例如JavaScript的XMLHttpRequest或更现代的获取(自2017年以来),或不建议使用的ActiveX对象。与大多数SPA框架的声明性方法相反,使用Ajax,网站直接使用JavaScript或JavaScript库(如jQuery)来操作DOM和编辑HTML元素。Ajax进一步被jQuery等库推广,jQuery提供了更简单的语法,并使历史上行为不同的浏览器之间的Ajax行为正常化。
WebSockets编辑
WebSockets是一种双向实时客户端-服务器通信技术,是HTML5规范的一部分。对于实时通信,它们的使用在性能[9]和简单性方面优于Ajax。
服务器发送的事件编辑
服务器发送事件(SSE)是一种服务器可以启动数据传输到浏览器客户端的技术。建立初始连接后,事件流将保持打开状态,直到客户端关闭。SSE通过传统HTTP发送,具有WebSockets在设计上缺乏的各种功能,如自动重新连接、事件ID和发送任意事件的能力。[10]
浏览器插件编辑
虽然此方法已过时,但也可以使用Silverlight、Flash或Java小程序等浏览器插件技术实现对服务器的异步调用。
数据传输(XML、JSON和Ajax)编辑
对服务器的请求通常会导致原始数据(例如XML或JSON)或返回新的HTML。在服务器返回HTML的情况下,客户端上的JavaScript会更新DOM(文档对象模型)的部分区域。返回原始数据时,通常使用客户端JavaScript XML / (XSL)进程(在JSON的情况下,使用模板)将原始数据转换为HTML,然后用于更新DOM的部分区域。
服务器架构编辑
薄服务器架构编辑
SPA将逻辑从服务器移动到客户端,Web服务器的角色演变为纯数据API或Web服务。在某些圈子里,这种架构转换被创造为“薄服务器架构”,以强调复杂性已从服务器转移到客户端,并认为这最终降低了系统的整体复杂性。
厚厚的有状态服务器架构编辑
服务器在页面的客户端状态的内存中保持必要的状态。这样,当任何请求击中服务器(通常是用户操作)时,服务器会发送适当的HTML和/或JavaScript,并进行具体更改,以使客户端达到新的所需状态(通常添加/删除/更新客户端DOM的一部分)。同时,服务器中的状态会更新。大多数逻辑都在服务器上执行,HTML通常也在服务器上呈现。在某些方面,服务器模拟网页浏览器,接收事件,并在服务器状态中执行增量更改,这些更改会自动传播到客户端。
这种方法需要更多的服务器内存和服务器处理,但优势是简化了开发模型,因为a)应用程序通常在服务器中完全编码,b)服务器中的数据和UI状态共享在同一内存空间中,不需要自定义客户端/服务器通信桥接器。
厚厚的无状态服务器架构编辑
这是有状态服务器方法的变体。客户端页面通常通过Ajax请求向服务器发送代表其当前状态的数据。使用此数据,服务器能够重建页面中需要修改的部分的客户端状态,并可以生成必要的数据或代码(例如JSON或JavaScript),这些数据或代码将返回给客户端以使其进入新状态,通常根据激发请求的客户端操作修改页面DOM树。
这种方法要求向服务器发送更多数据,并且每个请求可能需要更多的计算资源来部分或完全重建服务器中的客户端页面状态。与此同时,这种方法更容易扩展,因为服务器中没有保存每个客户端的页面数据,因此,Ajax请求可以发送到不同的服务器节点,而无需会话数据共享或服务器亲和力。
在本地运行编辑
SPA模型的挑战编辑
由于SPA是从浏览器最初设计的无状态页面重绘模型的演变而来,因此出现了一些新的挑战。可能的解决方案(复杂度、全面性和作者控制程度各不相同)包括:[12]
搜索引擎优化编辑
由于一些流行的网络搜索引擎的爬虫上缺乏JavaScript执行,[17] SEO(搜索引擎优化)历来给希望采用SPA模型的面向公众的网站带来了问题。[18]
2009年至2015年,Google Webmaster Central提出并随后推荐了“AJAX爬行方案”[19][20],在有状态AJAX页面的片段标识符中使用初始感叹号(#!
)。SPA网站必须实现特殊行为,以便搜索引擎的爬虫提取相关元数据。对于不支持此URL散列方案的搜索引擎,SPA的散列URL仍然不可见。这些“hash-bang”URI被包括W3C的Jeni Tennison在内的一些作家认为是有问题的,因为它们使那些没有在浏览器中激活JavaScript的人无法访问页面。他们还破坏了HTTP引用头,因为浏览器不允许在引用头中发送片段标识符。[21] 2015年,谷歌不建议使用他们的散列式AJAX爬行提案。[22]
或者,应用程序可以在服务器上呈现第一页加载,并在客户端上呈现后续页面更新。这在传统上很困难,因为渲染代码可能需要在服务器和客户端上使用不同的语言或框架编写。使用无逻辑模板、从一种语言交叉编译到另一种语言,或在服务器和客户端上使用同一语言可能有助于增加可以共享的代码量。
2018年,谷歌引入了动态渲染,作为希望为爬虫提供非JavaScript重版页面用于索引目的的网站的另一种选择。[23]动态渲染在客户端渲染页面的版本和特定用户代理的预渲染版本之间切换。这种方法涉及您的Web服务器检测爬虫(通过用户代理)并将其路由到渲染器,然后从渲染器中为它们提供更简单的HTML内容版本。
由于搜索引擎优化兼容性在SPA中并不微不足道,值得注意的是,SPA通常不用于搜索引擎索引是要求或可取的上下文。用例包括显示隐藏在身份验证系统后面的私有数据的应用程序。在这些应用程序是消费品的情况下,应用程序登录页面和营销网站通常使用经典的“页面重绘”模型,该模型为应用程序提供了足够的元数据,使应用程序在搜索引擎查询中显示为热门。博客、支持论坛和其他传统的页面重绘工件通常位于SPA周围,SPA可以使用相关术语为搜索引擎播种。
截至2021年,特别是谷歌,普通SPA的SEO兼容性很简单,只需要满足几个简单的条件。[24] 还提供了使用选择性预渲染的更高级SPA的实用指南。[25]
基于Java的ItsNat等以服务器为中心的Web框架使用的另一种方法是使用相同的语言和模板技术渲染服务器上的任何超文本。在这种方法中,服务器准确地知道客户端上的DOM状态,所需的任何大页面或小页面更新都会在服务器中生成,并由Ajax传输,Ajax是将客户端页面带到执行DOM方法的新状态的确切JavaScript代码。开发人员可以决定哪些页面状态必须由Web蜘蛛为SEO抓取,并能够在加载时生成所需的状态,生成纯HTML而不是JavaScript。就ItNat框架而言,这是自动的,因为ItsNat将客户端DOM树保留在服务器中作为Java W3C DOM树;在服务器中渲染此DOM树会在加载时生成普通HTML和Ajax请求的JavaScript DOM操作。这种二元性对搜索引擎优化非常重要,因为开发人员可以使用相同的Java代码和纯粹基于HTML在服务器中模板化所需的DOM状态;在页面加载时,传统的HTML由ItNat生成,使这种DOM状态与SEO兼容。
从1.3版本开始,[26]其Nat提供了新的无状态模式,客户端DOM不会保留在服务器上,因为使用无状态模式客户端,在根据客户端发送的所需数据在服务器上处理任何Ajax请求时,DOM状态会在服务器上部分或完全重建;无状态模式也可能与SEO兼容,因为SEO兼容性发生在初始页面的加载时,不受状态或无状态模式的影响。另一个可能的选择是PreRender、Pupeteer、Rendertron等框架,这些框架可以很容易地作为具有Web服务器配置的中间件集成到任何网站中,使中间件能够满足机器人请求(谷歌机器人和其他),而非机器人请求则像往常一样提供服务。这些框架定期缓存相关网站页面,以便搜索引擎可以使用最新版本。这些框架已获得谷歌的正式批准。[27]
有几个变通方法可以让它看起来像是可爬行的。两者都涉及创建单独的HTML页面,以镜像SPA的内容。服务器可以创建基于HTML的网站版本并将其交付给爬虫,或者可以使用PhantomJS等无头浏览器运行JavaScript应用程序并输出生成的HTML。
两者确实需要付出相当大的努力,最终可能会给大型复杂站点带来维护问题。还有潜在的搜索引擎优化陷阱。如果服务器生成的HTML被认为与SPA内容太不同,那么该网站将受到处罚。运行PhantomJS输出HTML可以减缓页面的响应速度,为此,搜索引擎——特别是谷歌——降级了增加服务器和客户端之间可以共享的代码量的一种方法是使用无逻辑模板语言,如胡子或手柄。此类模板可以从不同的主机语言呈现,例如服务器上的Ruby和客户端中的JavaScript。然而,仅共享模板通常需要复制用于选择正确模板并用数据填充它们的业务逻辑。当仅更新页面的一小部分时,例如大型模板中的文本输入值,从模板渲染可能会产生负面的性能影响。更换整个模板也可能扰乱用户的选择或光标位置,仅更新更改的值可能不会。为了避免这些问题,应用程序可以使用UI数据绑定或粒度DOM操作来仅更新页面的适当部分,而不是重新呈现整个模板。排名。[28]
客户端/服务器代码分区编辑
浏览器历史记录编辑
根据定义,SPA是“单个页面”,该模型使用“前进”或“返回”按钮打破了浏览器的页面历史记录导航设计。当用户按下后退按钮时,这会遇到可用性障碍,希望在SPA中出现上一个屏幕状态,但相反,应用程序的单页卸载,浏览器历史记录中的上一页会出现。
SPA的传统解决方案是根据当前屏幕状态更改浏览器URL的散列片段标识符。这可以通过JavaScript实现,并导致在浏览器中构建URL历史记录事件。只要SPA能够从URL散列中包含的信息中恢复相同的屏幕状态,预期的后退按钮行为就会保留。
为了进一步解决这个问题,HTML5规范引入了 pushState,并替换了State,提供了对实际URL和浏览器历史记录的编程访问。
分析编辑
Google Analytics等分析工具严重依赖浏览器中由新页面加载启动的整个新页面加载。SPA不是这样工作的。
第一页加载后,所有后续的页面和内容更改都由应用程序内部处理,应用程序只需调用一个函数来更新分析包。如果无法调用上述功能,浏览器永远不会触发新的页面加载,浏览器历史记录中不会添加任何东西,分析包也不知道谁在网站上做什么。
安全扫描编辑
与搜索引擎爬虫遇到的问题类似,DAST工具可能会在这些富含JavaScript的应用程序中遇到困难。问题可能包括缺乏超文本链接、内存使用情况以及SPA加载的资源通常由应用程序编程接口或API提供。单页应用程序仍然面临与跨站点脚本(XSS)等传统网页相同的安全风险,但也面临许多其他独特的漏洞,如通过API的数据暴露和客户端逻辑以及服务器端安全性的客户端执行。[29]为了有效地扫描单页应用程序,DAST扫描仪必须能够以可靠和可重复的方式浏览客户端应用程序,以便发现应用程序的所有区域并拦截应用程序发送到远程服务器的所有请求(例如API请求)。能够采取此类行动的商业工具很少,但此类工具肯定存在。[需要引用]
向SPA添加页面加载编辑
可以使用HTML5历史记录API将页面加载事件添加到SPA中;这将有助于集成分析。困难在于管理这一点,并确保准确跟踪所有内容——这涉及检查丢失的报告和重复条目。一些框架针对大多数主要分析提供商提供免费的分析集成。开发人员可以将它们集成到应用程序中,并确保一切正常,但没有必要从头开始做任何事情。[28]
加快页面加载速度编辑
有一些方法可以加快SPA的初始加载速度,例如选择性地预渲染SPA登录/索引页面、缓存和各种代码拆分技术,包括需要时的惰性加载模块。但不可能摆脱这样一个事实,即它需要下载框架,至少下载一些应用程序代码;如果页面是动态的,它将点击API获取数据。[28]这是一个“现在付钱给我,要么稍后付钱”的权衡方案。性能和等待时间问题仍然是开发人员必须做出的决定。