本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力
SPA
单页应用(SinglePage Application , SPA)
单页面应用SPA应用(single page application)就是用户通过某些操作更改地址栏url之后,动态的进行不同模板内容的无刷新切换,用户体验好。
第一次进入页面时会请求一个html文件,刷新清除一下,切换到其他组件,此时路径也相应变化,但是并没有新的html文件请求,页面内容却变化了。
原理: js会感知到url的变化,通过这一点可以用js动态地将当前页面的内容清除,然后将下一个页面的内容挂载到当前页面上。这个时候的路由不再是后端来做了,而是前端来做,判断页面显示相应的组件,清除不需要的。
优势
-
1 SPA 路由跳转是基于特定的实现(如 vue-router,react-router 等前端路由),而非原生浏览器的文档跳转(navigating across documents)。那么即可实现按需进行页面中的必要的组件级更新,而非 无差别式 页面级更新。
-
2基于 1 的特点,相较于 MPA 避免了不必要的整个页面重载,那么在页面切换之间的间隙更短,更能体现出 web 应用的 流畅 特点,因而更具有接近原生应用的 性能优势 与体验。
-
3因为组件级更新的特点,那么页面中的代码复用性高于 MPA。正是基于组件复用的特性,那么 SPA 更加适应需要快速迭代的产品。
-
4基于 SPA 的前端路由,使得 SPA 与应用后端解耦,使得前端不再依赖于后端的路由分配。即前后端分离。
缺点:
首屏时间稍慢,SEO差
-
1 SPA 应用在初始时是从 无状态 空白页面进入到 有状态 内容页面。而搜索引擎算法的抓取结果仅限初次请求时返回页面,搜索引擎是不会等待当前 SPA 进行 状态 填充。那么纯粹的 SPA 是不利于搜索引擎优化(SEO)。
-
2 父子组件必形成耦合,有 父 才有 子。在原则上,对比 开闭原则,每一次页面迭代,都需要修改组件内部代码,有引入 BUG 风险。
-
3 SPA 是整个应用页面,那么在未优化前端路由加载时,应用初始首屏即需要下载整个应用。这其中包含了一些用户根本不会在会话中访问的页面(但这些页面对于应用来说又是不可或缺的。)。这一点,相对于单个 MPA 组件来说,SPA 更 重 一点。
优化
SSR
基于以上特点,SPA 最大的优势就是基于 前端路由 实现组件级更新所带来的性能优势,在体验上 SPA 更为接近原生应用。但纯粹的 SPA 是从空白页面进行应用初始化。基于一般搜索引擎算法,从空白页面进行应用初始化是不利于 SEO 的。一个适应 SEO 的 SPA 必是需要通过 SSR(即 Server-Side Rendering)来进行应用优化,而 SSR 亦会增加了服务器的压力。基于此,SPA 要做到符合 SEO 的应用,必须要付出一些服务端的代价来换取良好的 SEO。
这里也存在一个 SPA 特例,即 静态 页面内容(如 产品介绍页)的 SPA。因为静态内容的不可变性特点,那么我们可以在服务端进行静态内容预渲染(pre-render),以此来减轻服务端不必要的即时页面服务端渲染需求的性能压力。预渲染的一种实现是使用 Chrome 浏览器的无界面 API puppeteer 并配合 prerender-spa-plugin 来实现静态内容的预渲染。在完成预渲染之后,即可得到 有状态 的 SPA,之后再将生成的页面部署在静态服务器上即可。此时这部分静态内容就避免了即时的 SSR。
因为动态内容(如 用户信息页)具有不确定性,那么这部分内容页面为了保证良好的 SEO 还是不可避免的需要 SSR。
动态加载路由组件
SPA 是基于前端路由来实现各级组件路由,那么在未优化 SPA 应用时,应用初始化就需要加载完成所有子路由基础组件。在这一点上,未优化的 SPA 相较于 MPA 首屏需要加载更多的基础内容。不论后期,用户是否会访问一些组件页面,在首屏加载时都会进行下载。那么此处无形中增加了应用初始化成本。那么 未优化的 SPA 相较于一般的 MPA 具有更高的初始启动成本。
我们常用的 SPA 首屏优化策略是使用 动态加载路由组件。那么即在应用初始,并不加载所有的子路由组件,而是在用户访问时再下载相应的子路由组件。本质上,我们将下载子路由组件的时间均摊到各个子路由组件自身,而不是在首屏集中处理。
常用的一种实现是 vue 下的 异步组件 配合 webpack 的 代码分割 以及 babel 转义(syntax-dynamic-import)草案的动态加载语法 import() 来实现动态加载路由组件。import() 默认在内部调用 Promise 函数,最终返回一个 Promise 对象,对于不支持 Promise 的浏览器,需要引入 es6-promise 或 promise-polyfill 或者直接引入 polyfill.io 动态引入 polyfill 来做兼容。
MPA
多页应用(MultiPage Application , MPA)
传统的项目大多使用多页应用结构(MultiPage Application, MPA),需要切换内容的时候我们往往会进行单个html文件的跳转,这个时候受网络、性能影响,浏览器会出现不定时间的空白界面,用户体验不好
优点
-
1 MPA 各个页面相互独立,那么可将每一个页面都看作一个单一的 微服务。各个页面达到相互独立与 解耦 的目的。
-
2 因其解耦特性,因而更加适合 前端去中心化 的复杂 web 应用。
-
3 因为页面互相独立的特性,那么有利于应用本地数据的模块化。
-
4 因为页面相互独立的特性,移除一个单页或增加一个单页不会对其他 MPA 单页造成影响。那么降低了我们页面迭代的门槛。不用担心对其他单页组件的 蝴蝶效应。
-
5 单个页面相互独立,且页面在初始时,就具有页面内容而非 无状态,那么相对于 SPA,更加有利于 SEO。
缺点
-
1 MPA 路由基于原生浏览器的文档跳转(navigating across documents)。因此每一次的页面更新都是一次页面重载,这将带来巨大的重启性能消耗。
-
2 MPA 的前端页面与后端是一一对应,耦合的。在开发时,增加了开发成本,前后端开发进度必须统一协作。
SPA 和 MPA 的比较
| 比较 | 多页面应用模式 (MPA) | 单页面应用模式 (SPA) |
|---|---|---|
| 应用组成 | 由多个完整页面构成 | 由一个外壳页面和多个页面片段构成 |
| 跳转方式 | 页面之间的跳转是从一个页面跳转到另一个页面 | 页面片段之间的跳转是把一个页面片段删除或隐藏,加载另一个页面片段并显示出来。这是片段之间的模拟跳转,并没有离开壳页面 |
| 刷新方式 | 整页刷新 | 页面片段局部刷新 |
| 跳转后公共资源是否重新加载 | 是 | 否 |
| URL模式 | xxx.page1.html xxx.page2.html xxx.shell.html#page1 | xxx.shell.html#page2 |
| 用户体验 | 页面间切换加载慢,不流畅,用户体验差,特别是在移动设备上 | 页面片段间的切换快,永辉体验好,包括在移动设备上 |
| 能否实现转场动画 | 无法实现 | 容易实现 |
| 页面间传递数据 | 依赖 URL、cookie 或者 localstorage 实现麻烦 | 因为在一个页面内,页面片段间传递数据很容易实现 |
| 搜索引擎优化 (SEO) | 可以直接做 | 需要单独方案做,邮电麻烦 |
| 特别适用范围 | 需要对搜索引擎友好的网站 | 对体验要求高的应用,特别是移动应用 |
| 开发难度 | 低一些,框架选择容易 | 高一些,需要专门的框架来降低这种模式的开发难度 |
结论 单页面应用模式由于有很多好处,已经是文本应用开发的潮流,特别是移动应用开发
如何选择使用SPA还是MPA
SPA 更加注重于接近原生应用的体验,基于代码组件的可复用特性,使得开发效率和成本方面具有得天独厚的优势,那么 SPA 更加适合开发有快速迭代需求的应用。而且基于可复用组件的特点,应用虽然在初始化时的成本高于 MPA,但其页面切换之间的成本是明显小于 MPA 的切换成本。虽然 SPA 因其架构特点导致其初始化成本高,但可以通过动态加载异步组件来显著降低 SPA 应用的初始化成本。
而对于 MPA 来说,更加注重于单个页面组件的之间的解耦,同时因为页面之间相互独立,那么使得 MPA 应用初始化成本小,但是页面重启的成本高。基于应用组件之间解耦的特性,MPA 更加适合开发大型复杂的 web 应用。在开发成本上来说,虽然代码复用性低于 SPA,但其解耦所带来的便捷拓展性,是 SPA 无法比拟的。MPA 无论是增加还是删除页面,对于其他的单页面组件影响都很小。但是,SPA 之间存在组件复用时,也就存在了代码耦合。特别是存在复杂逻辑的组件之间拓展功能时需要更加的小心翼翼。
虽然以上简要总结了 SPA 与 MPA 之间的差异,共同点以及他们的优化方式。但是我们在技术选型时,不仅需要考虑了以上因素,而且还需要结合特定的应用场景与自身的开发条件来选择合适的应用架构。MPA 与 SPA 没有说一定是要有对应的适用应用类型方式,因为应用场景,开发条件都是必须考虑进去的选型因素。
以上就是本篇的全部内容了,非常感谢帅哥美女们能看到这里,如果这个文章写得还不错或者对你有一点点帮助,求点赞,求关注,求分享,当然有任何问题可以在评论讨论,我都会积极回答的,再次感谢😁