Web Components 之旅:错误的道路、缺失的部分和有希望的路径
在我之前关于 Web 组件的博客文章中,我描述了几个至今仍未解决的难题。我收到的反馈出乎意料地热烈,而且我很高兴被初始提案的作者之一 Alex Russell提及。有些人认为我的笔记令人大开眼界,而另一些人则提到了更多问题。
然而,摘掉粉色眼镜并非我唯一的目标。我的批评旨在指出痛点,并提醒大家共识的重要性。Jan Miksovsky 最近发表的一篇关于 HTML 元素历史的博客文章<slot>
很好地说明了,如果我们再次推进这些提案,最终只会陷入与 5 年前相同的死胡同。
今天,我想进一步拓展我的视野,从稍微不同的角度探讨同一主题,探索 Web 组件在目前以及可预见的未来走向更广泛应用的道路。和以前一样,我基于个人经验和观察,所以如果您认为我遗漏了某些关键内容,请随时添加您自己的注释并纠正我。
关于上一篇文章,我还想提醒大家一件事,那就是即将举行的关于 Web 组件的“面对面”(F2F)会议,届时规范作者、浏览器厂商和用户都将参与其中。是的,议程中确实有一个专门讨论实际用例的环节。这是个好消息,我期待着会议记录发布后能尽快看到。
错误的方法
2016 年初,当我初次接触 Web Components 时,并没有太多的例子。当时,我唯一的灵感来源就是Polymer Starter Kit 。自那时起,它已经发生了很大变化,并被基于 LitElement 的后续产品PWA Starter Kit所取代。但简而言之,它们的作用是一样的:将<my-app>
组件放入index.html
。
将整个 SPA 封装到 Web 组件中,有些人开始梦想抛弃框架。在我看来,他们此时此刻正陷入使用不当模式的陷阱。在这里,我分享Mattia Astorino 提出的担忧。从我之前的文章中,你已经知道过度使用 Shadow DOM 可能会带来怎样的后果。
进一步说,每个 SPA 都需要的共同功能是客户端路由。没错,人们也一直在使用 Web 组件进行路由。在我之前的团队中,我们使用的是<app-router>
,这是一个 2014 年发布的原生 v0 自定义元素——也是我参与贡献的首批开源项目之一。当时,我非常喜欢它的声明式路由方式。
但今天听起来很荒谬:我们先在注册自定义元素时运行 JS,然后每次用户导航时,都从 DOM 中搜索匹配的路由并再次运行 JS。我错了吗?还是说这条链里还有额外的环节?声明性本身很好,但试图在任何需要的地方使用 HTML,或者像其他人使用 JSX 那样使用 HTML,又是一个严重的错误。
我们接下来要重新发明的框架专用轮子是一套用于加载数据和管理应用程序状态的工具。Polymer 社区在这方面一直缺乏最佳实践。简而言之,我们本来应该用它来<iron-ajax>
从 DOM 触发 API 请求,然后再将数据存储到 DOM 中——结果却把事情彻底搞砸了。
有人会说,一旦你被某个 Web 组件库牢牢束缚,就会倾向于把它用到极致。这可以解释为,Polymer 严重受限于 HTML 导入(感谢Mozilla放弃了这些功能),并且基于“模型驱动视图”概念的一些假设,而这种假设的灵感源于“声明式复兴”的梦想。
说到 HTML,HTML 模块提案仍然有机会提升开发者体验,并允许使用<template>
更多功能。这是 Edge 团队正在为 Chromium 做出贡献的领域之一。虽然这有助于更好地创建Web 组件,但我们不应该再采用那些错误的使用方式。
缺少的部分
回到 2019 年,Web 组件目前有哪些更好的用例?你首先想到的可能是 UI 组件,尤其是复杂的表单控件。令人惊讶的是,自定义元素目前并未设计为在提交标准<form>
HTML 元素时被拾取。Chrome团队提出的表单参与 API提案有望解决这个问题。
纵观自定义元素 API 的更新建议列表,似乎需要浏览器厂商同意并实施,这需要大量工作。在此之前,如果没有像<iron-form>
element 这样的变通方案,我们将无法使用 HTML 表单验证和提交。我认为,一旦这个问题最终得到解决,Web Components 将会覆盖更广泛的受众。
在表单中使用 Web 组件还存在一些其他问题。其中之一是Safari 中影子树内表单元素的表单自动填充功能失效。另一个问题是,像 LastPass 或 1Password 这样的密码管理器不支持 <input>
Shadow DOM 中的元素,因为它们可能依赖于全局 API,例如document.querySelector()
。
我们需要牢记的关于 Shadow DOM 的另一个问题是它如何影响可访问性。Rob Dodson 对此进行了描述,但我再重复一遍:影子树在文档中引入了 ID 的边界,这会影响 ARIA 属性,例如aria-labelledby
。这有时会使组件的可访问性和粒度变得更加困难。
ID 也存在类似的问题,当在 Shadow DOM 中使用SVG 时,内部引用会被破坏。例如,<use>
元素由于href
引用解析方式而无法正常工作。好消息是,Safari 至少有一个解决方案可以修复这个特定的 bug——但这个方案仍然需要被所有其他浏览器厂商采用,并最终体现在标准中。
如你所见,至少有两个看似完美、可以用 Web 组件解决的场景——表单元素和图标——已经被或多或少地阻塞了一段时间。在我看来,这些缺失的部分多年来一直未被覆盖(包括我在上一篇文章中提到的问题),这比其他任何事情都更不利于新标准的扩展。
充满希望的道路
那么,Web 组件最终如何才能被广泛采用呢?目前浏览器对原生组件的支持(至少在基础方面)已经很好了。我们真正缺乏的是在生产环境中(最好是在大型项目中)使用原生组件模型的真实案例,以及由此产生的最佳实践。这反过来又导致了人们对 Web 组件缺乏信任,如此循环往复。
当然,YouTube 就是一个例子,它同样是一个用 Polymer 构建的完整 SPA。但到目前为止,我们观察到的抱怨大多是关于它在 Firefox 和 Edge 上运行缓慢,这导致我们决定将 V0 规范的移除推迟到 Chrome 75 版本。最后,YouTube 仍然没有使用原生的 Shadow DOM,而是使用了 polyfill(并且未来很可能会继续这样做)。
我想重点介绍的另一个案例是 GitHub,它在放弃 jQuery 的过程中,转而使用自定义元素。尤其值得一提的是,自定义元素完美契合了将内容保留在 HTML 中并逐步增强的理念。details -dialog和detail-menu元素就是这种理念的很好例子。
继续寻找 Web 组件理论上可以搭上热潮的列车,如今的座右铭似乎是设计系统。可重用性和与框架无关性是我们不应低估的优势;这就是特斯拉选择 Web 组件的原因。随着时间的推移,我们将看到有多少人考虑做出同样的选择。
企业公司可能是 Web 组件最强大的受众,这一点我们从JS 2018 现状调查中可以看出——Polymer 的数据就是明证。值得一提的是,ING及其开源爱好者团队创建了open-wc,而 Red Hat 赞助了PatternFly元素库,这些都是这类大公司的例子。
一条颇有前景的途径是使用 Web 组件构建架构并嵌入应用程序的各个部分。这个概念被称为“微前端”。我们究竟应该在多大程度上将自定义元素用作“香辣 iframe”,这一点尚有争议。但至少我们应该记住,DOM 和 CSS 不再一定是全局的。
下一步是什么?
回到我们开始的地方,让我再次强调,Web Components 并非灵丹妙药。它们不会取代 JS 框架,尽管有些人声称相反,甚至开始与 React 说再见。事实上,这种带有偏见的言论通常弊大于利。这就是为什么我想为正在进行的讨论贡献我的看法,并推动它朝着正确的方向发展。
尽管存在许多需要进一步标准化的未解决问题(我已尽力将其系统化),但 Web 组件目前仍可安全使用。有备无患,我希望您有足够的勇气,甚至不必担心 Chrome崩溃或渲染问题(这些问题已经影响了多个稳定版本)。有些事情需要稳定,但毕竟没那么可怕。
David Flanagan 在最近关于 MDN Web 文档页面标题用 React 重写的“圣战”中承认,开发人员缺乏专业知识,这在我看来也算不上一个站得住脚的借口。只要大多数前端团队还在坚持使用大家熟知的技术栈,我们又如何去采用 CSS 网格布局,或者原生支持的 ES 模块呢?
所以,让我们接受挑战,并将其视为成为先锋的机会,利用 Web 平台提供的原生组件模型构建全新事物。如今已是 2019 年,是时候尝试一下自定义元素和 Shadow DOM 了。它们本身或许并非“下一个大热门”,但却是构建下一个大热门的坚实基础。
对于任何对长期解决方案感兴趣的人,我建议在启动新项目或重构现有项目时,根据实际需求考虑使用自定义元素、影子 DOM 或两者兼而有之。此外,我很想听听你使用 Web 组件的经验教训——包括你自己的痛点,因为我意识到它并非总是美好的。
最后,我鼓励大家为“ Web Components:正确之道”这个“超棒”的列表贡献力量。正如我之前宣布的那样,我是该项目的维护者,并竭尽全力使其成为一个或多或少完整且最新的知识库。并且,和往常一样,我非常乐意听到你们对这篇博文的反馈和评论。
链接:https://dev.to/webpadawan/the-journey-of-web-components-wrong-ways-lacking-parts-and-promising-paths-1d5a