DOM的渲染指的是对于浏览器中展现给用户的DOM文档的生成的过程。在Chrome中,这个渲染好的DOM,就是在开发者工具中元素这个tab中,递归的展开之后得到的整个文档。
DOM渲染的演化过程,大致可以分为可以分为三个阶段:
纯后端渲染
纯前端渲染
服务端的js渲染结合前端渲染
下面我们分阶段来做一下说明。
第一个阶段是纯后端渲染。采用这样的渲染方式,就是每一个页面中,在Chrome中展开得到的DOM,和服务器返回的DOM是基本一致的(可以通过查看网页源代码来得到服务器返回的DOM)。当然,这里是“基本”一致,因为实际操作中,页面或多或少还是会带有一些js代码,并且在浏览器端中运行这些js代码来对DOM进行的渲染,不过这一部分js代码并不影响DOM的主体是由服务端返回的。纯后端的DOM渲染,DOM树的生成完全是在后端服务器中完成的,相当于后端服务器的程序会把各种的数据拼成一个DOM树,并转换成一个字节流作为HTTP Response的body返回给浏览器。这种渲染的逻辑如下图所示:
纯后端的渲染的整体的结构是最简单的,把全部的逻辑都交给后端来完成。这样的优点在于返回的HTTP Response是包含着全部页面内容的,相对来说页面的主体DOM结构都会在这个响应中返回,可以让用户更快的看到页面的主体部分,而这样的响应对于浏览器爬虫来说也更有好,对SEO更有帮助。但是也正是由于这样的简单的结构,如果返回的DOM比较复杂,尤其是带有复杂的交互的页面,开发的难度就会非常大,或者说纯后端渲染很难带来良好的交互体验。当然,在纯后端DOM渲染中,地址的路由完全是由后端控制的(最简单的例子就是有后端直接把服务器上的静态目录结构返回回来),每一次路由发生变化,都会引起页面的刷新,这个使用体验其实也不是很好。
我们现在一般提到的“页面”这个概念或者说法,就是在纯后端DOM渲染的阶段中形成并且延续下来的。在纯后端的渲染中,可以很清晰的把一次DOM类型的HTTP请求作为一个页面。但是在后两种方式中,这个分界就远远么有这么清晰了,前端框架只是实现了类似的“页面”的效果,但是就不一定和某个具体的请求机制直接挂钩了。
第二个阶段就是纯前端渲染,很大程度上,纯前端渲染可以解决纯后端渲染中出现的各种体验问题。如下图所示,纯前端渲染把DOM生成的主题逻辑都放在了前端,这时后端只会返回一个框架的DOM结构,比如只带一个容器元素的的DOM,然后由js代码把页面的主题渲染到这个容器元素中。
在纯前端DOM渲染中,服务器主要是以API的形式返回各种数据,然后由js把数据重新组合成DOM。的大家耳熟能详的各种前端框架,比如Vue.js,React.js,angularjs等等,主要都是以这种方式完成了对于DOM的渲染。相对于纯后端的DOM渲染方式,纯前端DOM渲染的方式形成了一个天然的表现层和数据层的分界,js代码负责交互和展现,后端以API形式提供纯粹的数据。这种改变带来的最大的好处就是,交互的部分可以脱离数据接口独立的开发和调试,让站点的交互能力大幅的提升,并且很好的解耦了表现层和数据层的代码逻辑。
在纯前端DOM渲染中,第一屏的DOM渲染,依赖于大量的前端代码的加载和一次到多次的API请求。请求本身处理的时间,加上http请求的round trip的时间,这就会让第一屏的渲染之前有很多工作要做,并且每一步工作都带着从用户的网络到服务器网络的访问延迟,如果用户到服务器的物理距离很远,这个延迟累加起来就会很大。很多优化的方法,比如控制max-age等缓存时间,通过MANIFEST来储存静态资源等,都是针对用户第二次加载第一屏来进行的,无法解决用户第一次加载第一屏的问题。
下面我们就看一下第三个阶段,服务端的js渲染结合前端渲染,如下图所示
可以说,这里是把纯前端渲染划分成了两个渲染的子阶段。第一个子阶段即SSR初步渲染完成DOM,也就是把我们上面说到的第一屏先在服务器端通过js渲染出来,这个子阶段在服务器端增加了一个js渲染层的服务(比如next.js和nuxt.js),这一层相当于把原来要在客户网络与服务器网络之间进行的大量通信转移到了由服务器网络与服务器网络之间进行,大大缩减了网络通信消耗的时间。第二个子阶段就是前段渲染的阶段,主要是解决首屏加载之后用用户交互问题,这些都和纯前端渲染时完全一致的。
第三个阶段,主要是把前两个阶段中,一些交给纯后端DOM渲染逻辑分离的不好,但是交给纯前端DOM渲染又会造成较高延迟的部分单独分离出来形成了一独立DOM渲染阶段,保留的代码中天然的展示层和数据层的分离,又把API请求的累计延迟减少了很多,从SEO角度来说渲染结果对搜索引擎也很友好。当然,这样的做法需要给整体的架构增加一个独立的单元,给开发和部署都带来了更高复杂性。
本文提到的DOM渲染的三种方式,虽然第三个阶段最适于提供最优的使用体验,但不一定实际开发中最佳的方式,在特定的场景下,可以根据场景的特性来选择最简单的方法来完成。比如在网络延迟很低的情况下(内部网络),就没有必要选取服务器端js渲染的方式,纯前端渲染就可以带来低延迟的使用体验。
网友评论