组件:服务器端 vs. 客户端

Avatar of Sean C Davis
Sean C Davis

DigitalOcean 提供适用于您旅程每个阶段的云产品。 立即开始使用 200 美元的免费积分!

您在 2021 年构建网站吗? 我想您会采用 组件驱动开发方法。 这可是现在最热门的话题。 ReactVue 随处可见(Angular 还在吗?),而其他新兴框架不断试图抢占风头。

在过去的十年左右,我们见证了框架和工具的爆炸式增长,这些框架和工具帮助我们使用组件系统地构建网站。 早期的框架,如 AngularJS,帮助塑造了 Web 组件 的通用概念。 Web 组件也是用 JavaScript 编写的可重用 HTML 代码片段,由浏览器使其功能化。 它们是 *客户端组件*。

但从更通用的意义上讲,组件实际上已经存在了更长时间。 事实上,它们可以追溯到 Web 的早期。 它们通常不被称作 *组件*,尽管它们仍然发挥着组件的作用。 服务器组件也是可重用的代码片段,但在浏览器看到它们之前被编译成 HTML。 它们是 *服务器端组件*,并且在今天仍然非常流行。

即使在似乎到处都是“React,React,React”的世界里,两种类型的组件仍然具有相关性,可以帮助我们构建超级棒的网站。 让我们探索客户端和服务器组件如何彼此不同。 这将使我们更清晰地了解我们从哪里来。 然后,我们将拥有所需的信息来梦想着未来。

渲染

也许客户端组件和服务器端组件之间最大的区别在于它们是什么构成了它们。 那就是 *负责渲染它们的东西*。

服务器组件由 - *您猜对了!* - 服务器渲染。 它们通常不被称作 *组件*。 它们通常被称为 *部分*、*包含*、*片段* 或 *模板*,具体取决于它们使用的框架。

服务器组件可以有两种形式。 第一种是 *经典* 方法,即根据来自客户端的请求实时渲染组件。 请参见此处

Illustrated diagram of a browser making a request to an app server that requests components, which compile to a page and are returned to the app server to go back to the browser.
服务器端渲染的组件

第二种形式是 *Jamstack* 方法。 在这种情况下,整个网站在构建过程中被编译,并且在客户端请求时静态 HTML 已经可用。 请参见此处

An illustrated diagram showing the browser requests a page that has already been created, which consists of components that have gone through a build process from the server, then returns to the browser.
Jamstack 网站上的服务器组件已经编译成 HTML。

在这两种情况下,客户端(即您的浏览器)都不会看到您的组件之间的区别。 它只是从服务器接收一堆 HTML。

另一方面,客户端组件由 - *您已经猜对了两次,而且势不可挡!* - 客户端渲染。 它们是用 JavaScript 编写的,由客户端(您的浏览器)渲染。 由于服务器是服务器,并且它无所不知,因此它 *可以* 了解您的客户端组件,但它是否足够在意而对它们做些什么取决于您使用的框架。

与服务器组件一样,客户端组件也有两种形式。 第一种是更正式的 Web 组件,它使用 影子 DOM。 影子 DOM 有助于封装样式和其他功能(稍后我们将详细讨论)。 像 PolymerStencil 这样的框架使用影子 DOM。

像 React 和 Vue 这样的更流行的框架代表了组件的第二种形式,它们自己处理 DOM 操作和作用域。

交互性

由于服务器组件在发送到客户端时只是 HTML,如果它们要在前端具有交互性,则应用程序必须单独加载 JavaScript 代码。

考虑一个倒计时器。 它的呈现由 HTML 和 CSS 决定(我们稍后会回到 CSS 部分)。 但是,如果要使其正常工作(*计* 数),它还需要一些 JavaScript。 这意味着不仅要引入该 JavaScript,还要有一种方法让 JavaScript 附加到倒计时的 HTML 元素,这必须手动完成或使用(另一个)框架。

An illustrated diagram showing the browser requesting both a JavaScript bundler containing the JavaScript for the component, and a page with the component's markup that both are returned to the browser.
组件的 HTML 和 JavaScript 在 SSR 组件中是分开的。

虽然这可能感觉不必要地乏味(尤其是在您经历过足够长时间被 *强迫* 使用这种方法的情况下),但这样做却有好处。 这是明确的关注点分离,服务器端代码位于一个地方,而功能位于另一个地方。 它只引入了其交互性所需代码(理论上),从而减轻了浏览器的负担。

对于客户端组件,标记和交互性往往紧密耦合,通常位于同一文件或目录中。 虽然如果您不勤于保持组织,这很快就会变得混乱,但客户端组件的一大优势是它们已经可以访问客户端。 由于它们是用 JavaScript 编写的,因此它们的功能可以与它们的标记(和样式)一起发布。

A diagram showing the same flow as before, but both the component markup and JavaScript are included in the bundler, which then renders on the page.
客户端组件都包含在 JavaScript 代码中。

性能

在一对一的比较中,服务器端组件往往表现更好。 当浏览器接收到的页面包含它呈现所需的一切时,它将能够更快地将该呈现交付给用户。

Diagram showing the browser asking for a page of HTML in a single request.
从技术上讲,渲染 SSR 组件时您只需要一个请求。

由于客户端组件需要 JavaScript,因此浏览器必须下载或处理其他信息(通常位于单独的文件中)才能呈现组件。

Same diagram as before, but a bundler is requested by the browser along with the page.
客户端组件通常需要更多代码和请求。

也就是说,客户端组件通常在更大框架的上下文中使用。 React 有 GatsbyNext,而 Vue 有 Nuxt。 这些框架具有创建卓越的应用内体验的机制。 我的意思是,虽然它们加载您访问网站的第一个页面的速度可能较慢,但它们可以将精力集中在极快地交付后续视图 - 通常比服务器端渲染的网站交付内容的速度更快。

如果您在想,*是啊,但是 *关于预渲染和...*

是的,您说得对。 我们会说到那里。 此外,请不要再剧透了。 我们其他人都在体验旅程。

语言

服务器组件可以用(几乎)任何服务器端语言编写。 这使您能够使用与应用程序逻辑相同的语言编写模板。 例如,使用 Ruby on Rails 编写的应用程序默认使用 ERB 模板,这是一种 Ruby 形式。 因此,Rails 应用程序使用与应用程序本身相同的语言来编写其组件。

客户端组件是用 JavaScript 编写的,因为这是浏览器解析网站交互性的语言。 但是,JavaScript 也具有基于服务器的运行时,其中最流行的是 Node.js。 这意味着客户端组件的代码 *可以* 用与应用程序相同的语言编写,只要应用程序是用 Node(或类似语言)编写的。

样式(CSS)

在为组件设置样式时,服务器端组件会遇到与 JavaScript 相同的麻烦。 样式通常与组件分离,需要一些额外的努力才能将样式绑定到页面上的元素。

但是,像 Tailwind CSS 这样的框架正在努力使此过程不那么痛苦。

许多客户端组件库开箱即用地提供 CSS 支持(或至少是样式模式)。 这通常意味着将样式包含在与标记和逻辑相同的文件中,这可能会变得混乱。 但通常情况下,只要稍加努力,您就可以根据自己的喜好调整这种方法。

欢迎来到(混合)未来

两种类型的组件都不是单独的答案。服务器端组件在样式和交互性方面需要额外的努力,当我们看到客户端组件的提供功能时,这似乎没有必要。但另一方面,客户端组件往往会降低前端性能。由于网站的成功通常取决于用户参与度,性能低下会影响最终结果,甚至让人不愿意使用客户端组件。

这对需要同时兼顾性能和良好开发者体验的未来意味着什么?很可能,答案是**混合**方式。

组件必须在服务器端渲染。这是因为只有这样才能优化性能,而良好的性能将继续成为成功网站的一个属性。但现在,我们已经看到了使用框架(例如 React 和 Vue)轻松实现前端逻辑和交互性,这些框架将会继续存在(至少在一段时间内)。

那么,我们要去哪里呢?

我认为在不久的将来,我们将看到这些组件以三种方式结合在一起。

1. JavaScript 框架框架的进步

还记得你之前想到的关于预渲染的剧透吗?现在让我们谈谈它。

像 Gatsby、Next 和 Nuxt 这样的框架充当构建在组件框架(如 React 和 Vue)之上的前端引擎。它们整合了工具来使用它们首选的框架构建全面的前端体验。其中一个功能是预渲染,这意味着这些引擎会内省组件,然后在构建网站时在页面上写入静态 HTML。然后,当用户查看该页面时,它实际上已经存在。**他们不需要 JavaScript 来查看它。**

然而,JavaScript 通过一个叫做**水合**的过程发挥作用。页面加载后,用户看到所有(静态)内容,这时 JavaScript 开始工作。它接管组件,使它们具有交互性。这提供了使用一些服务器优势(即性能和 SEO)构建基于客户端组件的网站的机会。

由于这种方法,这些工具变得非常流行,我怀疑我们将看到它们继续发展。

2. 内置的客户端预渲染

这些复合词太多了。

过去几年我一直在思考的一个问题是:**为什么 React(或 Vue)不承担服务器端渲染?**它们确实可以,只是在没有其他框架帮助的情况下,很难理解或实现。

一方面,我理解单一职责原则,这些组件框架仅仅是构建客户端组件的方式。但我觉得把服务器端渲染委托给像 Gatsby 和 Next(以及其他)这样更大、更复杂的工具是一个巨大的失误。

好吧,React 已经开始朝着这个方向发展了。Vue已经做到了这一点Svelte从一开始就将这种方法作为优先事项

我认为,随着这些传统上专注于客户端的工具解决服务器端渲染问题,我们将看到更多开发。我怀疑这意味着我们将听到更多关于 Svelte 的消息,它似乎在这方面领先于其他框架。

这可能也会导致更多竞争者出现,以挑战 Gatsby 和 Next 这样的笨重工具。例如,看看 Netlify 正在使用他们的网站做什么。这是一个Eleventy项目,它引入 Vue 组件并在服务器上渲染它们。它缺少的是水合和交互性部分。我预计这将在不久的将来实现。

3. 服务器端组件交互性

我们仍然不能忽视服务器端组件的持续使用。另外两种进展的唯一副作用是,它们仍然使用 JavaScript 框架,而当你只需要**一点点**交互性时,这些框架会让人觉得没有必要。

一定有更简单的方法来添加**一点点**JavaScript,使用服务器端语言编写的服务器端组件更具交互性。

Basecamp 的团队似乎正在解决这个问题,他们刚刚发布了Hotwire,它是一种将客户端组件的一些优势带到服务器端的方法,使用(几乎)任何服务器端语言。

我不知道这是否意味着我们将立即看到 Hotwire 的竞争对手出现。但我认为 Hotwire 会引起一些关注。这可能会让人们回到使用像 Rails 这样的全栈单体框架。(就我个人而言,我很高兴 Rails 在这个以 JavaScript 为中心的时代并没有过时。竞争越多,网络就会变得越好。)

你觉得这些组件业务将走向何方?让我们讨论一下。