你可能不需要单页面应用

2019 年的前端,「框架 + 路由 + 状态管理」三件套似乎已经成为了应用标准配置,越来越多的工具把三件套作为基础设施,以至于越来越多的前端开发者开始习惯甚至依赖于这样的开发模式,单页面应用也就顺理成章的变成前端应用的标准形态。

然而,我们真的需要单页面应用吗?很多情况下答案是否定的。诚然,单页面应用已经被证明是开发复杂前端应用的有效模式,但问题的关键就在于:在我们的需求中究竟有多少场景可以称得上「复杂」。

单页面应用与多页面应用的对比

想要搞清楚单页面应用与多页面应用各自的适用场景,需要先了解它们各自的优势和劣势。

对于单页面应用,其优势显而易见:

  • 能够方便快速的在不同的视图间进行导航,并共享全局状态;
  • 全局状态的共享能够有效减少重复请求,降低服务端压力;

邮件系统就是一个典型的单页面应用的使用场景:用户经常需要在各个文件夹之间进行切换,邮件的状态需要在这些视图之间共享。除此之外,电商系统也可以被看作另一个适合的使用场景:用户需要经常在搜索、商品详情、购物车、订单结算等视图之间进行切换,并且伴随着状态的共享。

单页面应用的劣势也非常明显:

  • 需要引入前端路由,服务端 history fallback,项目复杂度增加;
  • 强依赖 CSS 模块化方案,解决视图之间可能产生的样式冲突;
  • 代码体积增大带来的页面加载性能的损失;
  • SEO 不友好;

进行单页面应用开发绝不是使用一个前端路由方案就能解决的问题,而是一个有一定复杂度的系统工程,这其中有 CSS 模块化、首屏性能、全局状态管理等等需要解决的问题。

与单页面应用相比,多页面应用的情况则完全相反:方案相对简洁、代码量更少、首屏性能更优;但对于视图导航只能卸载旧文档重新加载新文档,这样一来对于状态共享的实现也更加复杂。

经过上面的分析,相信大家已经对单页面应用和多页面应用各自的适用场景有了一些想法。对我而言,是否使用单页面应用模式的一个标准是当前的需求是否有一个紧密的业务上下文,也就是说用户是否会在一个业务场景的若干视图中进行频繁切换。如果当前需求是非常独立、上下文不明确的,那么多页面应用可能是更好的选择。

鱼和熊掌可以兼得

单页面应用和多页面应用在不同场景下有自身的优势。幸运的是,他们并不是互斥的,你可以将多页面应用中的一个页面转化为单页面应用,让这两种模式的优势能够充分发挥——这也是我所推荐的方式。

据我所知,这种方式在业界也已经被广泛使用。比如在 Github 中,网站的主体是一个由 rails 驱动的多页面应用,但在某些地方(比如项目主页、用户设置后台)则是一个嵌入的单页面应用。

多页面应用实践

这一部分通过一次对单页面应用进行多页面改造的实践,简单介绍一下如何实现一个多页面应用,以及多页面应用能够获得的首屏性能提升。

这次改造的项目是一个以 App 中 Hybrid 页面为主的项目。经过统计,项目中 3/4 的页面完全没有进行任何的导航操作,适合进行多页面应用改造。

项目是通过目前比较主流的 Webpack 进行打包构建的,Webpack 本身就支持多入口打包,因此能比较好的支持多页面应用。使用 glob 按照规则匹配文件后传入 Webpack 配置作为入口,完成这一步骤之后多页面打包的核心工作就已经完成了。接下来还需要为每个入口生成对应的 html,HTMLWebpackPlugin 默认会将所有入口都塞进一个 html 中,要生成多个 html,我们需要针对每个入口分别应用一个 HTMLWebpackPlugin,并且仅注入该入口的相关文件。

至此,一个多页面应用就可以跑起来了。当然,在整个搭建过程中还有许多细节配置需要根据项目的实际情况进行调整,这部分内容可以参考网上更详细的配置教程和 Webpack 文档,这里就不展开说明了。

根据改造完成后线上收集到的数据,多页面应用相对于单页面应用在 Page Load 数据上有 45% 左右的提升。

渐进式思想

文章的最后,我想要聊一聊技术架构及选型的思想及倾向性。开发过程中大大小小的架构及选型工作其实直接决定了项目的开发成本、维护成本、应用性能乃至影响项目的成败,因此在进行架构和选型的过程中的思想及倾向性就显得尤为重要。在这方面我一直推崇并实践的就是 渐进式思想,具体来说,应该选择一个恰好能满足目前确定需求的技术方案,并且保留扩展能力。这一思想主要是基于以下两点认识:

  1. 需求的不确定性以及技术的快速迭代
  2. 丰富架构一般比精简架构更容易

针对第一点,相信在互联网行业从事开发的同学都有深刻的体会:PM 告诉你要做一个 B 站,然而做到最后你却发现 PM 想要的是个淘宝。即使 PM 真的想要一个 B 站,经过几个月的迭代很可能会有更好的 B 站的实现方案,毕竟 B 站自己都已经开源了不是。

第二点也非常好理解,进行架构的精简往往都是破坏性的,并且伴随着巨大的风险,相比之下对架构进行丰富就更方便、更安全。

需要注意的是,渐进式并不代表可以更随意的做决策,恰恰相反,渐进式要求每次决策内容要减少,但是决策的质量要提高。渐进式成功实施的关键是确保足够的可扩展性,这在某种程度上对决策者提出了更高的要求。