下载本文的 PDF 版本 PDF

Web 组件化

我们可能正处在 Web 开发新革命的风口浪尖。


Taylor Savage

在当今的软件工程领域,没有哪项任务能像 Web 开发那样艰巨。

一个典型的 Web 应用程序规范可能会这样写道:该应用必须能在各种浏览器上运行。它必须以 60 fps 的帧率运行动画。它必须对触摸操作立即响应。它必须符合一套特定的设计原则和规范。它必须能在几乎所有可以想象得到的屏幕尺寸上工作,从电视和 30 英寸显示器到手机和手表表面。它必须是精心设计且长期可维护的。

随着最新的 Web 技术出现,这份列表还在增长:Web 应用程序必须能离线工作。它必须能够发送推送通知。它必须能在后台同步。

当然,并非所有新的 Web 项目都必须满足这整套要求——有些可能是更成熟的单页应用,另一些可能更侧重于发布或电子商务——但应用程序的这种多样性使得 Web 开发人员的工作更加困难。

当 Web 应用程序的期望遇到 Web 平台的现实时,真正的挑战就出现了。构建 Web 应用程序的原始材料远远没有跟上步伐。HTML 的构建块——例如“<div>”、“<p>”、“<h1>”和“<ul>”——非常适合文档标记,但它们不足以构建当今复杂的、类似应用程序的界面和网站。

因此,Web 开发社区已经发展出大量的框架来解决从平台提供的基本元素中构建合理界面的问题。可用 Web 框架的范围和多样性是巨大的——一个蓬勃发展的生态系统,只有像 Web 这样强大而灵活的平台才能支持。截至本文撰写之时,TodoMVC(图 1)12 上列出了 60 多个框架,这是一个框架使用情况的展示。这种蓬勃发展的框架生态系统确实令人惊叹。

Componentizing the Web: Partial list of frameworks showcased in TodoMVC

真是这样吗?

尽管许多 Web 框架都是工程上的惊人壮举,非常受欢迎,并且本身就拥有庞大的生态系统,但多框架模式存在一些关键问题,限制了 Web 开发人员的生产力。

框架是范围广泛、难以摆脱的依赖项。

HTML、CSS 和 JavaScript 固有的全局性是促使框架成为全方位工厂而非按需付费工具包的众多因素之一。选择 Web 框架通常是新 Web 项目中的第一个重大技术决策。由于框架往往是包罗万象的,几乎每一行代码或每一项标记都必须考虑到框架。例如,Angular 提供了复杂的视图管理和路由、依赖注入、国际化和可访问性功能、底层动画支持等等。Meteor 提供了整个前端和后端堆栈,从 UI 库到数据库驱动程序。这些本身就是功能极其全面的应用程序平台,但它们从一开始就导致锁定,并且极其难以迁移。如果您选择切换框架,您可能需要从头开始。

时尚性。 Web 框架来来去去,就像季节更替。图 2 显示了根据 Google Trends 统计的五个 JavaScript 库的受欢迎程度的兴衰。Web 开发社区不断渴望下一个新事物,这是理所当然的——Web 应用程序的需求以及设备和浏览器的功能发展如此迅速,工具也必须快速发展才能跟上。不幸的是,这意味着昨天热门的新框架可能就是今天的旧闻,而公司对某项技术的巨额投资可能会很快过时。为了在就业市场上保持竞争力,Web 开发人员必须跟上最新技术,在 Hacker News 框架公告、教程和入门套件的加速跑道上奔跑。

Componentizing the Web: Changing interest in various frameworks over time

缺乏互操作性。 一个健全的组件模型对于可扩展的界面开发至关重要。框架通常定义自己的组件模型来组织和渲染界面。Angular 有指令,而 React 和 Ember 各自都有自己关于“组件”的概念。然而,在一个框架模型中构建的组件在该框架之外没有任何意义——除非包含多个重叠且冗余的依赖项,否则您无法在 Ember 应用程序中使用 Angular 组件。再加上框架通常要求的锁定,缺乏互操作性使得在 Web 上编写通用可重用、封装的组件几乎不可能。

理想的、恰到好处的“金发姑娘”解决方案是,如果 Web 开发人员可以选择最适合他们要解决问题的应用程序架构,并且可以在项目之间重用界面组件。Web 开发社区可以为每个框架和组织理念拥有蓬勃发展的生态系统,以及一个共享的、通用的组件生态系统,该生态系统中的组件可以用于任何 Web 应用程序,而与框架无关。大型组织可以共享符合一致风格的全局维护组件集,但所有团队都可以使用,而与他们的技术栈无关。

这种 Web 开发的乌托邦似乎不可能实现,或者至少在技术上不可行,因为它需要不同框架之间就组件的一致处理达成广泛共识。然而,Web 在这些通用组件方面有先例——HTML 元素本身。

考虑 <SELECT> 元素。

<select> 元素提供了一个简单的下拉菜单。所有框架都理解并可以利用 <select>——它直接内置于平台中。它在所有浏览器中都能工作,并具有通常可预测的界面。它只做一项工作,并且做得很好。

此外,<select> 具有 API 表面,使其特别易于使用且有价值,如图 3a-f 所示。

它是可组合的(图 3a)。<select> 与用于其项目的“<option>”组合,生成一个完全形成的下拉菜单。

Componentizing the Web: a Composable select element

它是完全声明式的(3b)。可以使用标记中的属性应用各种不同的功能。

Componentizing the Web: Declarative attributes

它是灵活的(3c)。根据其子元素和属性,它可以提供不同的界面和功能。

Componentizing the Web: Flexible interface

它是容错的(3d)。不正确的子元素不会使应用程序崩溃,而只是被忽略。

Componentizing the Web: Forgiving syntax

它是内部可访问的(3e)。一旦获得焦点,它就会提供所有必要的句柄以便于访问。

Componentizing the Web: Accessibility to styles

除了它声明式提供的功能外,<select> 还可以通过脚本控制(3f)。它会发出可以监听和操作的事件,并且它具有可以利用的命令式 API。

Componentizing the Web: Scriptable events

最后,<select> 非常易于使用,并且几乎可以在任何上下文中使用。通过最少的声明式标记,开发人员就可以获得这种极其强大的行为。无论使用何种框架,这种简单的标记和 DOM API 都是众所周知且可用的。

“<select>”元素当然很有用,但平台仅提供有限的此类元素,并且该集合已经严重过时。有两种潜在的方法可以扩展这种能力:增加平台定义的元素的数量,或者为开发人员提供原语来创建他们自己的元素,使其具有原生元素的所有能力。

通过 Extensible Web Manifesto13,浏览器供应商果断地选择了后一种方法,倾向于提供扩展 Web 平台功能所需的原语,而不是直接在平台中提供更高级别的抽象。

构建像这样的元素需要什么?为了构建像 <select> 这样极其可重用的元素,开发人员可能需要平台的哪些原语和功能?

<select> 的几个关键部分使其非常有用。它具有属性形式的声明式 API、DOM 节点上的命令式 API、子元素方面(它支持的子元素)的组合模型以及标准的可视化界面。

因此,要创建类似的自定义元素,开发人员需要

定义其 API:为元素命名,并为其提供命令式方法以及可用于影响其行为的声明式属性。

定义其模板: 如果元素需要任何本地 UI,则为其提供一些基本的可视化布局。

将其与文档隔离: 元素的内部结构应该对文档不可见。也就是说,向文档添加元素不应产生意外的副作用。

定义其组合模型: 指定元素可以接受哪种类型的子元素以及它如何管理其子元素。

管理其依赖项: 自定义元素应该能够在本地 UI 中使用其他元素,因此应该能够指定和加载其依赖的任何元素的定义。

当然,您可以在框架级别构建这种封装和模板功能,许多框架也确实这样做了。但是,要实现广泛互操作组件的梦想,这些功能需要在平台级别提供,以便使用它们构建的组件可以像 <select> 一样重用。

进入 Web Components

Web Components 是少数几个新的 W3C 规范的总称,这些规范为开发人员提供了构建这种可互操作的平台级功能所需的原语。构成 Web Components 的各个规范几乎直接映射到创建真正可互操作元素所需的特定功能。

定义其 API:Custom Elements 规范2 描述了如何为元素命名、定义 API 表面以及响应其生命周期中的不同事件。自定义元素的注册可以归结为一个简单的调用

var MyElement = document.registerElement('my-element', {
  prototype: Object.create(HTMLElement.prototype)
});

这允许开发人员为元素指定标签,并传入元素所有实例的原型。此时,文档中所有“<my-element>”的实例都从 HTMLUnknownElement 升级到传入的元素的原型。请注意,自撰写本文时起,具体语法可能会略有变化 - 有关最新规范,请参阅 http://w3c.github.io/webcomponents/spec/custom/.

自定义元素规范中定义的生命周期回调为元素作者提供了在其生命周期的特定阶段对元素进行更精细控制的能力。这些回调包括“createdCallback”,在创建元素并已注册时调用;“attachedCallback”,在将元素插入文档时调用;“detachedCallback”,在从文档中删除元素时调用;以及“attributeChangedCallback”,在设置、更改或删除元素的属性时调用。

在第二个 document.registerElement 参数和 attributeChangedCallback 之间,元素作者可以指定元素的命令式和声明式 API。

定义其模板: 许多元素都有一些包含的 UI,例如按钮、输入框、选择框、标题和列表。为了构建真正的平台级元素,元素作者应该能够为其自己的元素指定 UI。Web 开发人员应该能够使用 Web 本身的语言——HTML 和 CSS——来定义元素的模板,而不是定义新的 DSL 或公开 C++ 钩子。

真正的模板具有一些关键属性。模板的仅仅存在不应对文档产生副作用,模板应该必须被显式选择才能使用,因此应该与主文档隔离,并且它应该在实际克隆和使用之前是惰性的。

Web 上的模板问题是所有 UI 框架都面临的问题。已经发展出许多解决方法来提供这种行为,但没有一种方法满足真正模板的所有标准。一些模板尝试使用主文档中的标记块,并使用“display: none;”来隐藏它,直到克隆和使用它,但这可能会产生布局和性能方面的副作用。将 HTML 粘贴到 <script> 标签中是另一种常见的模板方法,但这可能会导致 “.innerHTML” 的安全问题,并且在实际初始化为 DOM 之前操作起来很笨拙。

HTML5 <template> 元素11 提供了人们对真正模板所期望的完整功能集。它被解析但不渲染,并且在使用之前是惰性的,并且其内容以文档片段的形式与主文档隔离。这允许元素作者使用真正的模板来定义自定义元素的外观,以便在每次创建自定义元素并将其插入文档时进行克隆和使用。

将其与文档隔离: 模板为元素作者提供了一种将标记与元素关联的方法,但是一旦元素及其关联的标记插入到页面中,就需要有一种方法将它们与文档本身隔离。例如,HTML5 <video> 标签具有与其关联的播放按钮,但是标签的用户在使用 CSS 或“document.querySelector”在其主文档中时,不应担心意外地样式化或选择播放按钮。

Shadow DOM 规范9 为这种至关重要的封装提供了机制。它引入了“Shadow Root”的概念——一个独立的、作用域树,它存在于 DOM 中,但受到 CSS 选择器或 DOM 操作方法的意外干扰的保护。Shadow DOM 是封装原语,它允许元素在使用时无需担心副作用——元素意外地将其样式泄漏到其宿主文档,或者宿主文档意外地将效果或样式泄漏到元素。

Shadow DOM 是一个微妙而复杂但极其重要的原语。截至本文撰写之时,浏览器供应商仍在研究 Shadow DOM 作用域机制的细节,但对这种作用域原语的普遍需求已被广泛接受。

定义其组合模型: 为了像原生 HTML 元素一样工作,自定义元素必须能够接受和操作子元素。Shadow DOM 规范引入了“分发”10 的概念——指定阴影根内的插入点的能力,在这些插入点中,特定的子元素可以“分发”到主文档中。

这允许自定义元素作者定义元素接受哪种类型的子元素以及它如何与它们交互。作者可以使用它来指定“select”元素如何查找和投影其“option”子元素。从本质上讲,分发为元素提供了另一个 API 表面区域,以其接受的子元素的形式。

管理其依赖项: 借助通过模板和阴影根定义其自身内部标记的能力,人们可以想象自定义元素依赖于其他自定义元素来构建其内部 UI。由于多个元素可能依赖于同一个自定义元素,因此需要某种方法让元素声明其依赖项,并让浏览器加载和去重这种共享依赖项。

HTML Imports 规范3 提供了这种机制——元素作者或 Web 开发人员在 HTML 中加载基于 HTML 的依赖项的一种方式。截至本文撰写之时,规范作者正在努力将这种 HTML 加载和去重机制与即将到来的 ES6 模块加载和去重机制相协调。

借助这四个至关重要的新功能——Custom Elements、Templates、Shadow DOM 和 HTML Imports——Web 开发人员终于拥有了创建真正可重用自定义元素的平台级原语,它们具有原生 HTML 元素的所有功能。

如何利用 Web 组件——构建基于组件的 UI

问题仍然存在,个人和组织如何从 Web Components 提供的功能中获益?

Web Components 最直接的用例是在构建用户界面方面。软件工程中的通用最佳实践规定系统应该是隔离的和组件化的。现在可以直接将此指南应用于在平台级别构建 Web UI,并将自定义元素作为组件。

构建使用 Web 组件作为界面元素的新型 Web 产品的第一个步骤自然是构建整个产品将共享的一组自定义元素。由于自定义元素可以封装自己的外观,因此此元素创建步骤可能包括构建一组视觉上一致的按钮、数据表、菜单、布局模板和其他 UI 组件,作为要在应用程序或应用程序套件中使用的元素。

将前端工程团队的工作单元视为组件,而不是将工作单元定义为屏幕或流程,开始释放 Web Components 的组织力量。通过让每个人专注于一次构建单个自定义元素,团队可以最大限度地减少视觉上的不一致和重复工作。界面中元素的所有外观——例如,应用程序中使用的所有按钮——都是单个自定义元素的重用,并且在外观上保持一致。

单个工程师也可以从最大限度地减少在构建应用程序逻辑和 UI 之间切换上下文的成本中获益。通过预先花费时间来确保元素设计完美,而与应用程序的工作方式无关,像素级完美从最后时刻的润色变为应用程序开发中的必要阶段。随后的应用程序创建阶段也更加精简。从编写应用程序逻辑代码的第一行开始,应用程序看起来和感觉上都是完整的。在整个开发过程中,更容易感受到最终应用程序的样子,从而有助于尽早发现用户体验缺陷并开始有用的 QA 流程。

在团队的第二个项目上,基于自定义元素的界面模型的有效性更加强烈地体现出来。由于他们已经花费时间构建了一组像素级完美的界面元素,因此构建第二个界面的成本大大降低。这使团队能够专注于新功能和整体性能,而不是重新发明 UI。对元素的任何改进(来自第二个项目)都可以无缝地合并到第一个项目中。每个产品都成为一项资本投资,在整个自定义元素集的生命周期内都会产生红利。

重用元素的工程实践也与从旧项目复制和粘贴前端布局到新项目中有着本质的不同。从一开始就独立设计的自定义元素,专门用于灵活重用。旧项目中的原始布局代码或标记很少被设计为在不同的上下文中使用,并且可能充满了错误和怪癖。总体的样式表缺乏封装,并且会迅速积累冗余代码并变得难以维护。如果需要更新元素以适应新的用例,那么这将成为对自定义元素的资本投资,这将使所有未来的用户受益。如果需要更新原始布局代码,它通常会成为对不系统的编辑拼凑而成的补丁之上的又一个 hack,并迅速退化为意大利面条式代码。

一套统一的 UI 元素也有助于确保设计和工程团队之间的协同和效率。重用的元素确保品牌一致性,保证在任何地方都具有完全相同的外观。因此,视觉品牌不仅在设计层面上得到强制执行,而且在实现层面上也得到强制执行。一组元素提供了一个实时的样式指南,并且使工程与设计更容易对齐:设计不再在真空中发生,因为每个视觉元素和调整都可以快速集成到组件中并在现场进行测试。视觉设计改版也更容易实现。自定义元素只需要重新设计一次,新的样式就可以快速地在所有地方实现,通常只需对元素定义进行简单的升级即可。

组织还可以从将自定义元素创建与元素使用分离中获益。一些工程师会更适合实现元素的像素级完美设计,因为他们对动画有眼光,了解平台怪癖,并且对视觉细节充满热情。通过让这些工程师专注于创建将在许多应用程序中使用的自定义元素,他们的技能可以在全公司范围内得到利用。

良好的 UI 设计和性能通常既是艺术又是科学,并且可能需要艺术家的触觉才能恰到好处。自定义 UI 元素允许这种艺术成就得到广泛共享和利用。自定义元素有助于使简单的事情变得容易,并使困难的事情易于重复。拥有一套统一的 UI 元素的广泛而长期的优势使其成为工程组织早期投资的明显选择。

也许最重要的是,由于基于 Web Component 规范的自定义元素是使用平台而不是使用特定框架构建的,因此它们可以重用,而与下一个项目使用什么结构框架无关。对一组视觉上一致的元素的资本投资比为一种特定框架技术构建的一组组件的资本投资持续时间更长。

当然,强大的力量伴随着巨大的责任。原生 HTML 元素由浏览器供应商构建,内置了可访问性功能。使自定义元素可访问的责任落在元素作者身上。正如原生 HTML 元素如果不自然地可访问是不完整的,自定义元素也必须尽可能地内置可访问性功能。在某种程度上,生态系统动态应该奖励自然可访问的元素,但元素作者社区有责任从第一天起就明确优先考虑可访问性。自定义元素不是魔术般的可访问性魔杖——高质量的元素将在内部可访问,但应用程序作者也必须在应用程序级别正确处理可访问性。

Web 组件生态系统

除了对单个团队或组织的好处之外,人们可以想象完全可互操作的 Web 组件生态系统可能产生的网络效应。

可以创建自定义元素套件,以简化在 Web 上构建功能齐全的应用程序:针对不同类型的 Web 应用程序的不同“UIKits”。可以为特定用例构建自定义元素,例如使博客更易于创建的元素,或使电子商务站点更有效且更易于使用的元素,或使数据可视化更易于实现的元素。自定义元素可以通过使形式和功能能够交织到一个元素中,为真正语义 Web 的运动注入新的活力。目录可以帮助组织蓬勃发展的元素生态系统,众包元素评级和评论。

这种基于平台级互操作性的生态系统将需要广泛采用平台级 API,这些 API 使 Web 开发人员能够创建自定义元素。Web Components 规范是一项重大的事业。自 2011 年推出以来,它们引发了热烈的讨论,并根据反馈不断发展。尽管在 Web 组件的价值方面达成了普遍共识,但在规范的两个重要的有争议部分正在由实现者解决——将 HTML Imports 与即将到来的 ES6 模块系统协调一致,以及理顺 Shadow DOM 行为的细节。您可以在 public-webapps 邮件列表8 上关注并加入对话。

目前,Template 元素是 HTML Living Standard 规范11 的一部分,并得到现代浏览器的广泛支持。HTML Imports、Shadow DOM 和 Custom Elements 一直受到越来越多的跨浏览器热情,尤其是在最近的会议解决了更多有争议的部分之后。它们已从 Chrome 36 开始完整发布。Microsoft Edge 最近宣布4 它正在开始开发 HTML Template 元素,并对剩余规范的最新发展表示积极看法。Firefox 正在标记下发布实现,并且最近发表了一篇关于 Web 组件历史的深入文章5,并充满希望地得出结论,我们正接近广泛的跨浏览器支持。

6 这些 JavaScript 实现支持 Custom Elements、HTML Imports 和 Shadow DOM,支持从 IE10、Safari 7 以及常青浏览器 Chrome 和 Firefox 开始的主要浏览器的最新两个版本。基于 Web Components 的库(例如 X-Tag、14 Polymer7 和 Bosonic1)依赖于一些 polyfills 来实现广泛的浏览器支持,并包括围绕 polyfills 较重部分的优化,以实现生产就绪的性能。

今天的 Web 开发人员处境艰难。但是,凭借健全的平台级组件模型的一致性以及 Web 生态系统的狂野、广阔的力量,我们可能正处在 Web 开发革命的风口浪尖。组件化快乐!

参考文献

1. http://bosonic.github.io/.

2. http://w3c.github.io/webcomponents/spec/custom/.

3. http://w3c.github.io/webcomponents/spec/imports/.

4. Leithead, T. 和 Eicholz, A. 2015. Microsoft Edge and Web Components. https://blogs.windows.com/msedgedev/2015/07/15/microsoft-edge-and-web-components/.

5. Page, W. 2015. The state of Web Components. https://hacks.mozilla.ac.cn/2015/06/the-state-of-web-components/

6. https://github.com/webcomponents/webcomponentsjs.

7. https://www.polymer-project.org/1.0/.

8. https://lists.w3.org/Archives/Public/public-webapps/.

9. https://w3c.github.io/webcomponents/spec/shadow/.

10. https://w3c.github.io/webcomponents/spec/shadow/#distributions

11. http://www.w3.org/TR/html5/scripting-1.html#the-template-element.

12. https://github.com/tastejs/todomvc.

13. W3C Extensible Web Community Group. 2013. Extensible Web Manifesto: https://extensiblewebmanifesto.org/.

14. http://x-tags.org/.

Taylor Savage 是开放 Web 平台团队的产品经理,也是 Google Polymer 项目的首席产品经理。在加入 Polymer 之前,Taylor 曾在 Google 搜索的新功能方面担任产品经理。他毕业于斯坦福大学,获得计算机科学学位。

acmqueue

最初发表于 Queue vol. 13, no. 8
数字图书馆 中评论本文





更多相关文章

Shylaja Nukala, Vivek Rau - 为什么 SRE 文档至关重要
SRE(站点可靠性工程)是一种职位职能、一种思维方式和一套工程方法,用于使 Web 产品和服务可靠地运行。SRE 在软件开发和系统工程的交叉点运作,以解决运营问题并设计解决方案,从而可扩展、可靠且高效地设计、构建和运行大规模分布式系统。成熟的 SRE 团队可能拥有与许多 SRE 职能相关的明确定义的文档体系。


Arie van Deursen - 超越页面对象:使用状态对象测试 Web 应用程序
Web 应用程序的端到端测试通常涉及通过 Selenium WebDriver 等框架与 Web 页面进行复杂的交互。隐藏此类 Web 页面复杂性的推荐方法是使用页面对象,但首先需要回答一些问题:在测试 Web 应用程序时,您应该创建哪些页面对象?您应该在页面对象中包含哪些操作?给定页面对象,您应该指定哪些测试场景?


Rich Harris - 消除准入壁垒
一场战争正在 Web 开发领域展开。一方是工具制造者和工具用户的先锋,他们以摧毁糟糕的旧观念(在这个环境中,“旧”意味着任何在一个多月前在 Hacker News 上首次亮相的东西)和关于转译器等喧嚣的辩论为乐。


Alex Liu - JavaScript 和 Netflix 用户界面
自推出以来的二十年里,JavaScript 已成为 Web 的事实上的官方语言。在野生运行环境的数量方面,JavaScript 胜过所有其他语言。如今市场上几乎每一种消费硬件设备都以某种方式支持该语言。虽然这最常见是通过集成 Web 浏览器应用程序来完成的,但许多设备现在也以原生方式支持 Web 视图作为操作系统 UI(用户界面)的一部分。





© 保留所有权利。

© . All rights reserved.