第 2 章 同构 JavaScript 图谱

第 2 章 同构 JavaScript 图谱

Maxime Najim

根据客户端和服务器端代码共享程度的不同,可以将同构 JavaScript 划分出一个图谱(如图 2-1 所示)。在图谱的左侧,客户端和服务器端共用最低限度的视图渲染(如 Handlebars.js 的模板),以及某些名字、日期或 URL 的格式化代码,或者是应用逻辑的某些部分。在图谱的这一侧,我们通常会发现客户端和服务器端通过共用模板的方式共享视图层,并共用辅助函数(如图 2-2 所示)。这些应用需要的抽象程度不高,因为在 JavaScript 中已经有一些流行的库支持在客户端和服务器端之间共用代码,比如 Underscore.js(http://underscorejs.org/)和 Lodash.js(https://lodash.com/)。

图 2-1:同构 JavaScript 图谱

图 2-2:共享视图层

在图谱的右侧,客户端和服务器端共享整个应用(如图 2-3 所示)。共享内容包括整个视图层、应用程序流、用户访问限制、表单验证、路由逻辑、模型和状态。这些应用需要的抽象程度更高,因为客户端代码的执行上下文是 DOM(document object model,文档对象模型)和 window,而服务器端代码的执行上下文是一个请求 / 响应对象。

图 2-3:共享整个应用

 在本书的第二部分中,我们将深入了解客户端和服务器端的代码共享机制。但本章将通过介绍在客户端和服务器端共享代码的功能不同的几种应用,简要地探索图谱两端的机制差异。我们也将明确指出需要的某些抽象,以便这些功能同构地工作。

2.1 共享视图

通过减少用户在跳转页面时重新加载完整页面的次数,并在用户交互时采用渲染页面部分内容的方式,SPA 提供了更加流畅的用户体验。SPA 利用客户端模板引擎技术来接收模板(模板中包含了一些简单的变量占位符)、传递模型上下文对象、执行并输出 HTML,最终将结果插入到 DOM 树中。客户端模板将视图标记从视图逻辑中分离出来,从而创建更加易于维护的代码。而同构应用中的共享视图意味着模板和相对应的视图逻辑都需要共享。

2.1.1 共享模板

为了获得更快的(感知)性能和更佳的 SEO 效果,我们希望服务器端像客户端一样能够渲染任意的视图。在客户端,模板渲染很简单,只需要对一个模板进行求值,并将输出插入到某个 DOM 元素即可。但在服务器端,同一个模板会被渲染成字符串并在响应中作为结果返回。同构视图渲染的棘手之处在于,客户端需要接手完成服务器端未完成的事情。这通常称为从客户端到服务器端的过渡(client/server transition),也就是说,应用在浏览器中加载完成后,客户端需要进行适当的转换,以免“破坏”服务器端生成的 DOM 字符串。服务器需要将应用状态提取到一个对象中(称为 dehydrate),并发送给客户端,随后客户端需要使用同一状态初始化应用,并将视图还原(rehydrate)为与在服务器上相同的状态。

例 2-1 展示了一种典型的服务器响应,即在页面的 body 部分渲染某些标记,并且使用 <script> 标签输出经过序列化的状态。服务器将序列化状态放入渲染的视图中,客户端需要对状态进行反序列化,并将状态和预先渲染的标记关联起来。

例 2-1 引入服务器端渲染的标记与状态

<html>
  <body>
    <div>[[server_side_rendered_markup]]</div>
    <script>window.__state__=[[serialized_state]]</script>
    ...
  </body>
</html>

2.1.2 共享视图逻辑

模板的 helper 是对象,如数字、字符串或散列对象,通常比较容易共享。对于日期这样的格式化数据的共享,许多格式化库都同时支持在服务器端和客户端运行,比如 Moment.js 可以同时在服务器端和客户端进行日期的解析、验证、操作与显示。另外,URL 的格式化需要在路径前面添加主机名和端口,而在客户端中只需要简单地使用相对 URL 即可。

2.2 共享路由

大部分现代的 SPA 框架都支持路由的概念,路由负责在用户跳转页面时跟踪用户的状态。在 SPA 中,路由是控制跳转事件、改变状态和页面视图,以及更新浏览器跳转历史的主要机制。在同构应用中,我们同样需要一套路由配置(即将 URI 的模式映射到路由处理器中),而且这套配置能够在服务器端和客户端之间方便地进行共享。共享路由的难点在于路由处理器自身,因其经常需要访问与环境相关的 API 来获取 URL 信息、HTTP 头部和 cookie 等。在服务器端,这些信息可以通过请求对象的 API 取得,而在客户端则需要通过浏览器的 API 取得。

2.3 共享模型

模型通常被称为业务对象、域对象或者实体。通过移除状态存储并从 DOM 恢复,模型为数据建立了一种抽象。在最简单的实现中,同构应用可以使用服务器端返回首屏响应之前一模一样的状态,对客户端应用进行初始化。在同构 JavaScript 图谱的一个极端中,服务器端和客户端共享状态与模型的定义规范包括双向同步(第 4 章将对这种实现进行更为详细的探讨)。

2.4 小结

应用在同构 JavaScript 图谱中可以处于不同的位置。客户端和服务器端共用的代码量不尽相同,从共享模板到共享应用的整个视图层,再到共享应用的大部分逻辑。随着在同构 JavaScript 图谱中的位置不同,应用可能需要建立更多的抽象。在下一章中,我们将会讨论不同类别的同构 JavaScript,并且更深入地分析这些抽象。

目录