本文作者
侯策:硕士毕业于法国国立高等电信学校。曾任职于BePATIENT集团,负责互联网+医疗平台的研发。曾任职于法国能源和苏伊士集团,参与欧洲天然气运输和费用系统的研发。2015年回国加入百度知识搜索部,负责多个产品线的大型技术迭代。行业之外是一名国家二级运动员(足球项目),曾组织过赴北非撒哈拉地区看望孤儿等慈善活动。
颜海镜:知名技术博主,开源达人,常以歪脖无脸男形象作为头像活跃于各大技术网站,经过多年沉淀,专注Web前端开发,先后任职于金山、百度、美团点评,负责前端开发工作。
随着React和Redux为服务端渲染提供了优良特性,同构应用变得越来越普遍。作为开发者,即使采用的技术架构并不是基于服务端渲染的同构设计,也很有必要对同构设计进行了解并掌握其原理。
服务端渲染或直出的概念越来越流行。在了解如何基于React实现服务端渲染之前,我们有必要在架构层面对服务端渲染的“前世今生”进行整体了解:为什么会出现这样一个概念;这个概念落地之后能解决什么问题;服务端渲染和其他方式对比有何利弊等。
早期的Web开发,架构设计简单、直接,具体来讲,就是页面由JSP、PHP等工程师在服务端生成,浏览器只负责展现。那时候,前端工程师只需要给静态页面添加一些动态交互效果,很少会涉及数据逻辑等;而后端工程师负责页面内容,即当用户请求页面时,后端进行处理并返回完整的静态页面。这些过程一般会依靠模板引擎来完成。因此,在那个时候,甚至没有独立的前端工程师职位。即使有的话,这种做法的缺点也很明显,比如前后端分工职责不清。
如果由前端人员来开发模板,那么前端将会极度依赖后端环境,难以实现开发效率的最大化,同时关于数据格式的沟通成本也相对较高。另外,这样的架构模式对于前端技术的发挥和利用浏览器能力的空间是非常有限的。
随着前端技术的飞速发展,尤其是AJAX和Node.js等技术的出现,一种前后端分离的架构模式应运而生。在这种模式下,前后端分工变得非常清晰,两端的关键协作点是AJAX接口。下面我们以用户访问页面为例来一步步了解这种模式,如下图所示。
非服务端渲染访问流程图
在这种构架设计下,对页面的请求处理分为以下几个步骤。
这样的架构设计使得前后端开发可以并行进行,职责清晰——前端工作主要集中在浏览器端,前端开发只需要完成对测试数据的模拟,环境相对容易配置,能做到本地开发,脱离后端的支持;而后端专注于业务逻辑,负责API接口的实现。
然而任何技术架构和设计都不可能脱离时代而永远存在,技术的演进一定会随着发展愈演愈烈。这种架构模式在提升开发效率的同时,短板也很明显,比如不利于SEO(Search EngineOptimization,搜索引擎优化)和存在性能问题等。
SEO是一种通过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。如今,网民主要通过搜索引擎在网上查找信息和资源。SEO做得好,能够直接提升网站在搜索引擎搜索结果中的展现排名,更有利于页面的曝光。可是采用前后端分离的方式,由于页面的数据内容主要由JavaScript脚本动态生成,因此非常不利于搜索引擎获取该页面的信息,影响该页面的SEO。
另外,在这种架构设计下,我们会发现来自浏览器端的请求增多了,体现在用户体验上,就是用户必须等待JavaScript脚本加载完成,且真正执行时才会发起数据请求。接下来,等待数据成功返回后,脚本完成页面内容渲染,用户才可以得到最终页面。这样做直接降低了页面首屏展现的时间,特别是在移动互联网环境下,对首屏加载性能的影响很大。
为了解决上述问题,服务端渲染技术粉墨登场。
服务端渲染技术会把数据请求过程放在服务端,相对于前后端分离的方式,获取数据更加提前,页面模板结合数据的渲染处理也在服务端完成。结合React技术,基本的组件拼接在服务端完成,并最终输出相对完整的HTML返回给浏览器端。接下来,进一步的组件渲染将在浏览器端完成。具体流程如下图所示。
服务端渲染访问流程图
这样做的好处非常明显:当浏览器初次请求页面后,用户第一次拿到的HTML文档已经进行了初步内容渲染,这样必然更有利于SEO优化,也解决了首屏的性能问题。
我们发现总的请求数并没有改变,而是把浏览器的一部分数据请求移到了服务端。事实上,在服务端进行数据拉取的成本要远远小于浏览器端,而且传输更加高效,这也是性能提升的关键之处。
细心的读者可能会发现,这里所谓的服务端渲染与本章开始介绍的早期Web开发的服务端渲染传统模式并没有本质上的区别。事实确实如此,从某种程度上说,它是一种向传统模式的回归,不过这种回归并不是倒退,而是一种螺旋式的发展。事实上,依靠React实现的服务端渲染也并不是简单地渲染内容,在很大程度上它还实现了代码复用。
下面我们将“服务端渲染”一词替换为“同构”。其实,这两个词的背景和所表达的意义大体相同,但又有一定的差别:服务端渲染主要侧重架构层面的实现,而同构更侧重代码复用。
任何一种架构模式都是以服务业务需求为前提、以技术时代发展为背景的。它们各有利弊,具体采用哪一种模式,需要开发者深思熟虑,结合自身的实际情况进行选择。
随着Node.js的异军突起,前后端开发有了归一化编程语言的基础土壤,页面模版、第三方依赖机制等都有实现前后端统一的契机。React率先引领了这种潮流,同构的概念也因此得以更广泛的传播。
需要读者明白的是,同构应用并不是不需要浏览器端渲染内容,而是使服务端和浏览器端渲染达到一种平衡。那么,怎么理解这种平衡呢?
在服务器上生成渲染内容,让用户尽早看到有信息的页面。一个完整的应用除包括纯粹的静态内容以外,还包括各种事件响应、用户交互等。这就意味着在浏览器端一定还要执行JavaScript脚本,以完成绑定事件、处理异步交互等工作。
从性能及用户体验上来看,服务端渲染应该表达出页面最主要、最核心、最基本的信息;而浏览器端则需要针对交互完成进一步的页面渲染、事件绑定等增强功能。所谓同构,就是指前后端共用一套代码或逻辑,而在这套代码或逻辑中,理想的状况是在浏览器端进一步渲染的过程中,判断已有的DOM结构和即将渲染出的结构是否相同,若相同,则不重新渲染DOM结构,只需要进行事件绑定即可。
从这个维度上讲,同构和服务端渲染又有所区别,同构更像是服务端渲染和浏览器端渲染的交集,它弥补了服务端和浏览器端的差异,从而使得同一套代码或逻辑得以统一运行。同构的核心是“同一套代码”,这是脱离于两端角度的另一个维度。
同构的优势如下:
同构的劣势如下:
————
本文节选自博文视点新书《React状态管理与同构实战》。