隔离 DOM

2014 年 1 月 16 日

单页 Web 应用程序通常会变成 jQuery 混乱,其中应用程序逻辑、DOM 操作和服务器访问都混合在一起。这种关注点的混合使得此类应用程序比应有的更难理解和测试。隔离 DOM 是一种模块化策略,它将所有 DOM 操作分离到专用的 JavaScript 对象中。

例如,请考虑我最近开发的用于讨论我最喜欢的欧洲游戏的过滤目录页面。此页面的一个关键行为是您能够单击左侧的过滤面板来过滤在条目列表面板中显示的游戏。所有这些都是通过 JavaScript 完成的。我为过滤面板和条目列表创建了 JavaScript 类,每个类都封装了该页面部分的 DOM 访问。决定显示哪些游戏的逻辑保存在一个控制器类中,该类通过一个独立于 DOM 的接口与 DOM 操作类进行通信。控制器类不使用任何 jQuery。

在初始化期间,控制器将它的更新函数传递给 Filter DOM,以便在过滤器的任何框更改状态时调用它,Filter DOM 将此函数设置为复选框的单击事件。因此,当过滤器显示中的任何内容发生变化时,控制器中的此更新函数会向 Filter DOM 类询问它的活动过滤器 ➊。Filter DOM 类通过使用 jQuery 查找相应的 HTML 元素 ➋,过滤掉已选中的框,并向控制器返回一个简单的数据结构,该结构指示每个组中哪些框已选中 ➌。

控制器使用有关过滤器状态的信息来决定现在应该显示哪些游戏,将它们分成左右两列,并使用应该在列表中显示的元素 ID 的详细信息调用 Entry List DOM ➍。Entry List DOM 使用 jQuery 将 HTML 项目整理到正确的 div 中以完成工作 ➎。

这样做的主要好处是通过使每个类专注于一项任务来使其更容易推理。当我处理 Filter DOM 类时,我专注于如何对 HTML 元素执行 DOM 操作。当我处理控制器时,我可以忽略 HTML 结构、css 类名等的细节。

用更抽象的模式术语来说,这些 DOM 对象充当网关,两个边界上下文(应用程序和 HTML DOM)之间的通道。像任何网关一样,DOM 对象都有一个接口,它使用其客户端(应用程序)的词汇,以及一个将此翻译成超越的野蛮(HTML DOM)土地的实现。[1]

良好的模块化设计往往与可测试性相关。每个类都可以相对独立地进行测试。隔离 DOM 的一个特别优势是,我可以测试控制器而不使用浏览器或 PhantomJS 等模拟浏览器。由于我不需要 DOM 来测试控制器,因此我可以在节点中测试它,并为 DOM 网关提供一些简单的测试替身。这使得修改控制器变得更快更容易。通过尽可能多地将逻辑从 DOM 网关中移出,我可以增加在不诉诸 PhantomJS 等(谦虚对象模式的应用)的情况下可以进行的测试量。

进一步阅读

Pete Hodgson 是本文想法的宝贵来源。巧合的是,他自己的关于使用隔离 DOM 的文章在同一天发表。它包含更多细节和一个简短的示例。

致谢

Pete Hodgson 为我提供了一些有用的想法来改进本文。

笔记

1: 您可以对服务器访问使用类似的分离 - 这是网关的经典应用。