2021 年 Web 组件:优点、缺点和不足
Web 组件是一套原生功能,能够出色地控制样式和功能。它们既可以用于普通的、无框架的网页,也可以与任何你选择的 JavaScript 框架(例如 React、Vue、Angular、Svelte 等)配合使用。这使得 Web 组件非常适合构建需要公开共享或在多个项目中复用的可重用元素。至少理论上是这样。
实际上,Web 组件存在一些缺点,在某些项目中可能几乎无法使用。
在本文中,我将解释 Web 组件的优点和缺点,并提供一些指导,帮助您决定是否应该在项目中使用它们。
优点
Web 组件强大的两个主要特性是自定义元素 API和Shadow DOM。
自定义元素 API 允许您创建组件并将其注册为新的 HTML 元素。它还允许您为新元素定义生命周期回调。总的来说,它非常出色,而且对于新手和经验丰富的开发人员来说都相当容易理解和上手。
Shadow DOM 负责提供样式封装。它为组件赋予独立的 DOM 环境,使其与文档的其他部分完全分离。这意味着全局样式无法影响它(CSS 自定义属性/变量除外),并且其自身的样式也无法影响父文档中的其他元素。
HTML<template>和<slot>元素也用于大多数自定义元素中,使您可以轻松创建具有动态内容的模板,而无需使用第三方模板系统或语言。
浏览器对所有这些功能的支持都很好:除非你还在使用 Internet Explorer,否则不太可能遇到任何重大问题。不过也有一个例外,我们将在后面的“缺点”部分进行解释。
此外,正如文章开头所述,Web 组件不仅几乎兼容所有主流的 JavaScript 框架,而且无需框架即可在原生 JavaScript 中使用。这是因为 Web 组件本质上是扩展了原生 HTMLElement 的 ES6 类。这意味着您可以在项目内部或公司整个生态系统中共享这些组件。
此外,还有一些很棒的库和软件包可以简化构建 Web 组件的过程,还有一个在线平台,您可以在该平台上查找 Web 组件并与他人共享:webcomponents.org。
缺点
未样式化内容的闪烁
我们先来看看自定义元素 API。我使用自定义元素遇到的唯一缺点是可能会出现未样式化内容的闪现。由于自定义元素是在 JavaScript 中声明和注册的,因此加载、处理、注册和最终渲染可能需要几毫秒的时间。在此期间,您的自定义元素将处于未样式化或隐藏状态。
对于营销网站而言,这可能是一个主要缺点,因为你只有几秒钟的时间与访客互动以保持他们的注意力;但在 Web 应用程序中,这并不是什么大问题,尤其是在初始加载后,浏览器的缓存会大大缓解这个问题。
以下是一个在没有缓存的情况下重新加载页面(在本地开发服务器上)时,使用“选项卡容器”Web组件出现FOUC的示例:
以下是重新加载页面后同一组件的渲染结果,使用了浏览器缓存(仍然在本地开发服务器上):
如您所见,浏览器缓存使得重复访问时不会出现此问题。
Shadow DOM 与原生表单兼容性不佳。
我遇到的 Web 组件的最大问题是它们与原生表单功能完全不兼容。这主要有两个原因:
- 自定义元素不能扩展除 以外的元素
HTMLElement(除非使用繁琐的变通方法,并且存在重大缺陷); - Shadow DOM 内部的表单元素不会被组件的父表单视为表单元素。
还记得 Shadow DOM 不使用全局样式吗?这意味着,如果你想在 Web 组件内部使用`<div> `、 `<div>` 、 ` <div>`、 `<div> `、 `<div>` 等<form>元素的样式,你需要在组件的样式表中重新定义每个元素的样式。<input><select><textarea><button><label><fieldset>
当然,您可以将这些元素分别做成独立的 Web 组件,让它们各自封装自己的样式。但是,由于表单元素(例如 `<input>`)HTMLInputElement无法被自定义元素扩展,您的自定义输入组件必须将 `<input>` 包含<input>在其 Shadow DOM 中。而这正是下一个问题所在:Shadow DOM 中的输入元素(以及其他表单元素)不被视为表单的一部分。
例如,如果表单的提交按钮位于 Shadow DOM 中,则除非您添加自己的keydown事件监听器来复制此功能,否则无法再通过在输入框中按 Enter 键来提交表单。
这里还有一个稍微复杂一些、也更有启发性的例子。如果你想创建一个自定义输入框,有三种解决方案:
- 您可以在常规 DOM 中,在自定义元素旁边生成一个
<input type="hidden">,并手动复制一系列内置功能,以确保您的输入始终正确同步、触发正确的事件、正确验证、可访问、外观良好且运行良好。 - 您可以将每个表单元素(包括表单
<form>本身)都做成一个独立的 Web 组件,从而<form>在整个项目中放弃使用原生元素。 - 使用 Javascript 处理所有使用此自定义输入元素的表单。
如果你已经身处一个大量使用 JavaScript 的环境中,每个表单都通过 JavaScript 处理,并且每个组件的实现都需要大量的工作才能可用和可访问,那么这可能看起来不像是一个大问题。
但是,如果您更倾向于传统方式、是 Web 开发新手,或者只是喜欢简单的解决方案和环境,这很可能是一个重大决定性因素。
我想构建的 Web 组件中有相当一部分需要以某种方式与表单或表单元素配合使用,我估计大多数其他开发人员也是如此。
丑陋的
最糟糕的是,网上几乎没有关于如何修复或规避与原生表单不兼容问题的任何信息。
像Shoelace这样的 Web 组件库只是简单地实现了自己的自定义表单元素,这些元素必须在 Javascript 中手动处理。
旨在帮助构建 Web 组件的库(例如 Google 的Lit)无法扩展内置元素,因为 Safari 不支持对内置元素进行自定义。
我们的立场是什么,以及你是否应该使用它们
总的来说,在我满怀热情地踏上 Web 组件之旅仅仅几周/几个月后,我发现自己虽然并不悲观,但对 Web 组件的现状及其在 Javascript 框架项目和生态系统之外的未来略感失望。
我仍然认为 Web 组件的理念和总体实现方式都很棒。但是,当涉及到原生表单时,它们的缺点使得学习和实现起来都困难得多。
你应该使用 Web 组件……
- 如果您已经在使用 Javascript 手动处理所有表单。
- 如果您有(或计划有)多个具有不同技术栈的项目或生态系统,并且需要在其中共享/重用组件,那么您需要考虑以下情况:
- 如果您不介意在真正开始开发自己的业务相关功能之前,花费大量时间重新实现内置功能和辅助功能(或者如果您可以使用像Shoelace这样的现有组件库来节省初始开发时间和成本),那么
- 或者,如果您不需要组件与表单或表单元素进行交互。
你不应该使用 Web 组件……
- 如果您想保留使用原生表单的功能
- 如果您需要支持旧版浏览器
远处的灯光
这篇文章刚发表不久,@westbrook就留言告诉我ElementInternals 规范目前已在 Google Chrome 中实现(但 Safari 和 Firefox 尚未支持)。一旦所有浏览器都支持该规范,它或许能有效解决我在文章中提到的表单相关问题。
请查看以下文章,了解有关此规范、其实现以及可用 polyfill 的更多信息:
最后还有一件事……
如果您不在 JavaScript 密集型环境中,但仍然想在表单中使用 Web 组件(例如:您正在构建 Laravel 或 Symfony Web 应用程序),您始终可以开发一个通用表单处理程序来克服本文中描述的问题。
当然,这比直接使用原生表单要复杂得多,而且在开始之前还需要进行一些开发和测试,但这可能是最简单的解决方法。
如果您有其他变通方法或解决方案,欢迎在评论区或推特上分享。
文章来源:https://dev.to/emileperron/web-components-in-2021-the-good-the-bad-and-the-ugly-3kg