当前位置: 首页 > Web前端 > HTML

【译文】Web渲染概述

时间:2023-04-02 18:23:48 HTML

本文简单介绍了Web应用的各种渲染方案,包括客户端渲染、服务端渲染等渲染方案。文章翻译自:https://developers.google.com...。由我团队联合翻译发表于前端技术公众号:FangstoolCollection,转载于此。房僧雅集是阿里CBU前端技术的专业号,感兴趣的朋友可以关注一下。1.扬帆起航作为开发人员,我们经常面临影响应用程序整个架构的决策。我们必须做出的核心决策之一是在哪里实现业务逻辑和渲染逻辑。这可能很困难,因为构建网站的方法有很多种。对这个领域的理解来自于我们过去几年在一些大型网站上的工作。从广义上讲,鼓励开发人员选择具有再水化(在下一节中解释)或静态渲染的服务器端渲染。为了在做出决定时更好地理解我们选择的架构,我们需要对每种方法和术语有扎实的了解,不同渲染方法的页面性能可以帮助我们理解它们之间的差异。2.术语渲染:SSR:服务器端渲染。CSR:客户呈现。补水:基于服务端渲染的dom树和数据,浏览器端使用JavaScript再次渲染。预呈现:在构建时生成静态HTML和页面的初始状态。性能:TTFB:TimetoFirstByte——从浏览器发送资源请求到接收到资源的第一个字节的时间。FP:FirstPaint——从页面打开到呈现视觉内容的第一个像素的时间。FCP:FirstContentfulPaint——从页面打开到页面主要内容可见的时间。TTI:TimeToInteractive——页面打开进入交互状态的时间。这里只是简单介绍一下这些性能术语。如果你想详细了解它们,你可以阅读我们之前写的两篇文章。指标中心(b)。3.服务器端渲染服务器端渲染:当页面打开时,服务器生成完整的HTML并返回。这样就避免了在浏览器端获取和渲染的成本,因为这些事情在服务器响应用户之前就已经完成了。服务器端渲染会带来快速的FP和FCP,在服务器端处理业务逻辑和渲染逻辑,可以避免向浏览器发送大量的JavaScript,有助于实现快速的TTI,这种方式适用于各种设备和网络环境,如果启用一些流式浏览器优化,例如流式文档解析。对于服务器端渲染,用户不太可能等待浏览器执行其他消耗CPU的JavaScript代码再继续,因为页面内容已经显示。当无法避免第三方js(打广告)时,服务端渲染虽然可以减少FP、FCP渲染的JavaScript消耗,但可能会给接下来要执行的js带来一定的“负担”。服务端渲染的主要缺点是渲染需要时间,所以TTFB可能比较大。服务器渲染是否足以满足应用程序的需求在很大程度上取决于您正在构建的体验类型。关于哪个更好的服务器端渲染或客户端渲染的争论从未停止过。但我们需要记住的是,我们可以选择让一些页面在服务器端呈现,而另一些在客户端呈现。一些网站使用这种混合渲染模式取得了不错的效果。比如Netflix的登录页面使用了服务端渲染,同时对于交互性强的页面预取js,对那些交互性强、客户端渲染的页面进行页面加速。加载的机会。许多现代框架、库和体系结构使同一个应用程序可以同时在服务器和浏览器中呈现。虽然这些技术可用于服务器端渲染,但需要注意的是,用于在服务器和客户端上渲染的架构具有非常不同的性能特征和权衡。React用户可以使用它的renderToString方法或者其他基于该方法的框架进行服务端渲染,比如Next.js;Vue用户可以参考其服务端渲染指南或Nuxt;Angular用户可以参考Universal。4.静态渲染静态渲染:在构建(build)时,直接渲染页面中不会变化的内容,然后打入HTML。在浏览器端需要执行的js有限的假设下,该方法可以提供快速的FP、FCP和TTI。与服务器端渲染不同,它还提供了一个快速的TTFB,因为不需要在服务器端生成HTML。一般来说,静态渲染需要为每个URL生成一个单独的HTML。用户访问时,直接返回预渲染的HTML即可。另外渲染出来的HTML也可以部署到CDN,通过边缘缓存(edgecaching)做一些优化。不了解边缘缓存的同学可以看一下我们之前写的React缓存笔记,里面有关于它的介绍。静态渲染也有不同的方案,像Gatsby这样的工具旨在让开发人员感觉他们的应用程序是动态渲染的,而不是在构建过程中生成静态HTML;Jekyl和Metalsmith等工具包含静态属性,提供许多模板驱动的方法。静态渲染需要为每个URL生成单独的HTML,这是它的缺点之一。如果你无法提前预知这些URL的内容,或者一个网站有大量的URL,那么静态渲染可能就不合适了。对于静态渲染,React用户可能熟悉Gatsby、Next.js静态导出或Navi。这里需要重点理解静态渲染和预渲染的区别:静态渲染的页面只需要执行很少的js代码,浏览器就可以交互了;虽然预渲染可以实现FastFP、FCP,但是应用必须在浏览器端执行js代码才能变得交互。如果你不确定是使用静态渲染还是预渲染,那就测试一下,在应用程序加载时禁用JavaScript加载和执行。禁用JavaScript后,对于静态渲染,页面的大部分功能仍然可以正常工作;对于预渲染,只有页面的一些基本功能可能起作用,例如连接跳转。另一个有用的测试是通过Chrome的DevTools减慢网络速度,以查看在页面变为交互之前下载了多少JavaScript。预渲染通常需要更多的JavaScript,并且通常比具有渐进增强的静态渲染更复杂。5.服务端渲染VS静态渲染服务端渲染不是灵丹妙药,其动态特性会带来巨大的计算开销。服务器端渲染会增加TTFB或发送双重数据(例如将客户端状态数据键入HTML)。在React中,renderToString会很慢,因为它是同步的和单线程的。为了使服务器端渲染“正确”工作,我们需要关注组件缓存、内存消耗、memoization技术的应用等问题。很多时候,您可能需要多次渲染同一个应用程序——一次在服务器上,一次在客户端上。服务端渲染只能让需要显示的内容更快,并不能减少工作量。服务器端渲染可以根据不同的URL生成不同的内容,其速度可能比静态渲染慢。其实我们可以做一些工作来缓解这个问题,服务端渲染+HTML缓存可以大大减少渲染时间。服务端渲染的优势在于可以获取实时数据,并且可以处理比静态渲染更大的一组请求。因为静态渲染只能处理可以提前预知内容的页面,静态渲染无法处理需要个性化(千人千面)的页面。在构建PWA时,服务器端渲染也很有用。服务器端渲染页面碎片,前端使用ServiceWorker进行缓存。6.客户端渲染客户端渲染:是指直接在浏览器中使用JavaScript渲染页面,所有的逻辑、数据获取、模板、路由都在客户端处理,而不是在服务端。在移动端,客户端渲染很难实现并保持较快的渲染速度。有时我们只需要做一点工作就可以让客户端渲染的性能和服务端渲染的性能几乎一样,比如保持尽可能小的JS体积和尽可能小的RTT(https://en.wikipedia.org/wiki...)。即使使用HTTP/2服务器端或使用推送关键脚本和数据,我们也可以更早地开始解析工作。此外,像PRPL这样的技术可以帮助我们加快页面的初始化和后续导航的速度。但事情并没有那么简单。客户端呈现的主要问题是所需的JavaScript随着应用程序的增长而增长。随着新的JavaScript库、兼容组件和第三方代码的添加,控制脚本的大小变得极其困难——尤其是因为这些代码和库通常需要在页面内容呈现之前加载。所以对于那些脚本量很大的应用,应该优先考虑代码拆分的方案。特别是对于延迟加载的JavaScript,请确保仅在需要时才加载必要的代码。对于交互很少或没有交互的应用程序,服务器渲染是一种更具扩展性的解决方案。如果你想构建一个SPA(单页)应用程序,使用AppShell来缓存与页面交互的核心组件将对你有很大帮助。如果结合PWA的ServiceWork技术,还可以有效提升页面重复访问的性能。7、通过复水将服务端渲染和客户端渲染结合起来,通常称为同构渲染或简称为“SSR”。这种方法试图在客户端渲染和服务端渲染之间找到一个平衡点,希望能同时减少前者的缺点。当页面导航导致跳转或刷新时,服务器会输出页面的HTML文档,并将页面需要的javascript和数据(用于渲染)内联到文档中一起输出。如果实现得当,这种方法确实可以像服务端渲染一样实现更快的首次内容绘制(FirstContentfulPaint),然后客户端会通过一种叫rehydration的技术继续渲染(在客户端)。这是一种新颖的技术,但它会导致比较大的性能问题。使用reydration技术进行服务器端渲染的主要问题是它会对交互时间(TimeToInteractive)产生显着的负面影响,尽管它缩短了首次绘制的时间(FirstPaint)。服务端渲染的页面往往让人感觉已经加载完毕,可以开始交互了,但实际上它只能响应用户交互(比如用户输入行为),直到客户端js脚本执行完毕,DOM事件发生绑定完成。在一些移动终端上,这个过程会持续几秒甚至几分钟。也许您自己也遇到过这种情况:页面似乎已加载,但是当您在页面上执行单击或点击时,没有任何反应。这很快变得令人沮丧……“为什么(页面)没有响应?为什么我不能滚动?”Rehydration问题:反复Rehydration的问题还不止于此,通常比js造成的交互延迟还要糟糕。为了让客户端js能够准确渲染而不需要重新向服务端请求渲染所需的数据,目前的服务端渲染通常会将UI需要的数据序列化,内联到HTML文档的script标签中.最终的HTML文档包含更高层次的重复:如您所见,对于页面导航请求,服务器返回相应的UI描述(HTML),但它也返回呈现UI所需的数据。同时,客户端脚本中还包含了对UI的描述(译者注:前端渲染需要包含对UI的描述,比如JSX),以便在客户端继续渲染。只有当bundle.js完成下载和执行后,UI才会进入交互状态。从一些使用补液方案的真实网站收集到的性能数据来看,极不推荐该方案。原因是回到用户体验上:这种方式很容易让用户停留在“神秘峡谷”中。(译者注:也就是界面可见但不能交互的状态)尽管如此,外界对于补水方案还是有一些期待的。简单的说,当使用服务端渲染时,只针对需要高度缓存的内容减少首字节时间(TTFB),并获得类似于预渲染的结果。未来可能会逐步或部分应用补水方案,成为服务端渲染的关键。8、流式服务器端渲染和渐进式补水服务器端渲染在过去几年取得了长足的进步。流式服务器端呈现允许您以块的形式发送HTML,而浏览器接收并逐个呈现它们。该方案可以实现快速的FP和FCP。React提供了一个异步的、流式的方法renderToNodeStream,与同步的renderToString相比,它可以更好地处理服务器压力。GradualRehydration也值得关注,React团队正在做一些有趣的探索(https://github.com/facebook/r...)。使用这种方法,随着时间的推移“触发”服务器呈现以呈现应用程序的各个片段,而不是当前一次呈现整个应用程序。这有助于减少使页面具有交互性所需的JavaScript数量,因为它可以延迟页面上低优先级内容(例如首屏内容)的呈现,同时防止这些低优先级呈现阻塞主线程.而且,这种方案还可以通过Rehydration避免服务端渲染的一个大陷阱:服务端渲染生成的DOM树被销毁,然后在浏览器端重建。这个问题多半是客户端同步渲染引起的。DOM树所需的初始数据尚未准备好。局部补液事实证明,局部补液很难实现。这个方案是progressiveRehydration的扩展,需要分析独立片段(components/views/trees)的哪些部分很少或没有交互进行progressiverehydration。对于这些近乎静态的部分,相应的JavaScript代码会转化为惰性引用,从而将它们对客户端的影响几乎降为零。部分水合对缓存提出了一定的挑战,客户端导航意味着我们不能假设应用程序的惰性部分,即服务器呈现的HTML,在页面完全加载之前可用。三态渲染如果ServiceWorkers是你的一个选择,“三态”渲染也是一个有趣的想法。使用这种技术,您可以使用流式服务器端渲染来生成初始或无JS部分,然后在安装ServiceWorker后使用它来渲染html。该解决方案使缓存的组件和模板保持实时,还支持基于不同导航在同一会话中呈现不同视图的SPA类型应用程序。这种方法可以在服务器端、客户端和ServiceWorker上重用模板和路由代码。9.SEO在选择渲染方案时,通常会考虑SEO。通常我们选择服务端渲染来对付爬虫。爬虫可能理解JavaScript,但它们有很多局限性,我们需要关注它们是如何呈现的。客户端渲染虽然可以工作,但是一般没有测试等工作。如果您的应用程序依赖于客户端呈现,动态呈现是一个值得考虑的选项。有关详细信息,请参阅https://developers.google.com....当有疑问时,MobileFriendlyTest可以测试您选择的方法是否按预期工作,它可以显示页面在爬虫中的显示方式,序列化的HTML内容(在JavaScript执行之后),以及渲染期间发生的任何错误。MobileFriendlyTest地址如下:https://search.google.com/tes...10.总结在决定用什么方法进行渲染时,我们必须知道我们即将遇到的瓶颈是什么。使用客户端渲染还是服务端渲染将决定你90%的架构设计。一个完美的解决方案通常是从服务端发送html和minimaljs来完成交互。下面是服务端-客户端渲染的总结图: