Web 组件现状

Web 组件已经出现在开发人员的雷达中相当一段时间了。它们最初由 Alex Russell2011 年 Fronteers 大会 上介绍。这个概念震惊了社区,并成为未来许多演讲和讨论的主题。

2013 年,一个名为 Polymer 的基于 Web 组件的框架由 Google 发布,旨在对这些新 API 进行测试,获取社区反馈并添加一些糖衣和观点。

到目前为止,已经过去了 4 年,Web 组件应该无处不在,但实际上,Chrome 是唯一一个具有某种版本 Web 组件的浏览器。即使使用 polyfill,也很明显,在大多数浏览器都支持 Web 组件之前,社区不会完全接受它们。

为什么花了这么长时间?

简而言之,供应商无法达成一致。

Web 组件是 Google 的一项工作,在发布之前,几乎没有与其他浏览器进行协商。就像生活中的大多数谈判一样,没有参与感的各方缺乏热情,并且往往不会达成一致。

Web 组件是一个雄心勃勃的提案。最初的 API 级别很高,并且实施起来很复杂(尽管有充分的理由),这只会加剧供应商之间的争端和分歧。

Google 推动了这项工作,他们寻求反馈,获得了社区的认可;但事后看来,在其他供应商发布之前,可用性被阻碍了。

Polyfill 意味着理论上 Web 组件可以在尚未实现的浏览器上运行,但它们从未被接受为“适合生产使用”。

除此之外,由于 Edge 的工作(即将完成),Microsoft 无法添加许多新的 DOM API。而 Apple 一直专注于为 Safari 开发替代功能。

自定义元素

在所有 Web 组件技术中,自定义元素争议最小。人们普遍认同,能够定义 UI 组件的外观和行为以及能够跨浏览器和跨框架分发该组件的价值。

“升级”

术语“升级”是指元素从普通的 HTMLElement 转换为具有定义的生命周期和 prototype 的闪亮自定义元素时。今天,当元素被升级时,它们的 createdCallback 被调用。

var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() { ... };
document.registerElement('x-foo', { prototype: proto });

到目前为止,来自多个供应商的 五个提案 中,有两个最具希望。

“Dmitry”

一种 createdCallback 模式的演化版本,它与 ES6 类配合良好。createdCallback 概念依然存在,但子类化更传统。

class MyEl extends HTMLElement {
  createdCallback() { ... }
}

document.registerElement("my-el", MyEl);

与今天的实现类似,自定义元素最初以 HTMLUnknownElement 的形式存在,然后在未来的某个时间点,其原型被替换(或“交换”)为注册的原型,并且调用 createdCallback

这种方法的缺点是它与平台本身的行为不同。元素最初是“未知的”,然后在未来的某个时间点转变为最终形式,这会导致开发者感到困惑。

同步构造函数

由开发者注册的构造函数在解析器创建自定义元素并将其插入树的时刻被调用。

class MyEl extends HTMLElement {
  constructor() { ... }
}

document.registerElement("my-el", MyEl);

虽然这看起来很合理,但这意味着如果包含 registerElement 定义的脚本是异步加载的,则初始下载的文档中的任何自定义元素都将无法升级。在进入异步 ES6 模块的世界时,这不利于此。

此外,同步构造函数还存在与 平台问题 相关的 .cloneNode()

预计供应商将在 2015 年 7 月的面对面会议上决定一个方向。

is=””

is 属性使开发者能够将自定义元素的行为叠加在标准内置元素之上。

<input type="text" is="my-text-input">

支持论点

  1. 允许扩展内置元素的未公开为原语的功能(例如,可访问性特征、<form> 控件、<template>)。
  2. 它们提供了一种“渐进增强”元素的方法,使其在没有 JavaScript 的情况下仍然可以正常工作。

反对论点

  1. 语法令人困惑。
  2. 它回避了我们在平台中缺少许多关键的可访问性原语 的根本问题。
  3. 它回避了我们没有办法正确扩展内置元素的根本问题。
  4. 用例有限;一旦开发者引入 Shadow DOM,他们就会失去所有内置的可访问性功能。

共识

人们普遍认为,is 是 Custom Elements 规范上的一个“疣”。Google 已经实现了 is,并将其视为在公开更低级的原语之前的权宜之计。目前,MozillaApple 更愿意尽快发布 Custom Elements V1,并在 V2 中正确解决这个问题,而不会用“疣”污染平台。

HTML 作为自定义元素 是 Domenic Denicola 的一个项目,它尝试使用自定义元素重建内置的 HTML 元素,以试图发现平台中缺少的 DOM 原语。

Shadow DOM

Shadow DOM 迄今为止是供应商之间争议最大的功能。严重到不得不将功能拆分为“V1”和“V2”议程,以帮助更快地达成协议。

分发

分发是指将 Shadow 主机子元素在视觉上“投影”到主机 Shadow DOM 中的插槽的过程。这是使您的组件能够使用用户在其内部嵌套的内容的功能。

当前 API

当前 API 完全是声明式的。在 Shadow DOM 中,您可以使用特殊的 <content> 元素来定义您想要将主机子元素视觉插入的位置。

<content select="header"></content>

AppleMicrosoft 都反对这种方法,因为他们担心复杂性和性能问题。

新的命令式 API

即使在 面对面会议 上,也无法就声明式 API 达成一致,因此所有供应商都同意寻求命令式解决方案。

所有四个供应商(MicrosoftGoogleAppleMozilla)都承担了在 2015 年 7 月截止日期之前指定此新 API 的任务。到目前为止,已经 提出了三个建议。其中最简单的一个看起来像这样

var shadow = host.createShadowRoot({
  distribute: function(nodes) {
    var slot = shadow.querySelector('content');
    for (var i = 0; i < nodes.length; i++) {
      slot.add(nodes[i]);
    }
  }
});

shadow.innerHTML = '<content></content>';

// Call initially ...
shadow.distribute();

// then hook up to MutationObserver

主要障碍是:时机。如果主机节点的子节点发生变化,并且我们在 MutationObserver 回调触发时重新分发,那么请求布局属性将返回错误的结果。

myHost.appendChild(someElement);
someElement.offsetTop; //=> old value

// distribute on mutation observer callback (async)

someElement.offsetTop; //=> new value

调用 offsetTop 将在分发之前执行同步布局!

这似乎并非世界末日,但脚本和浏览器内部机制通常依赖于 offsetTop 值的正确性来执行许多不同的操作,例如:将元素滚动到视图中。

如果这些问题无法解决,我们可能会看到回到声明式 API 的讨论中。这将采用当前 <content select> 样式的形式,或者采用新提出的 “命名插槽” API(来自 Apple)。

新的声明式 API - “命名插槽”

“命名插槽”提案是当前“内容选择”API 的一个更简单的变体,组件用户必须明确地用他们希望将其分发的插槽标记其内容。

<x-page> 的 Shadow 根

<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>
<div>some shadow content</div>

<x-page> 的使用

<x-page>
  <header slot="header">header</header>
  <footer slot="footer">footer</footer>
  <h1>my page title</h1>
  <p>my page content<p>
</x-page>

组合/渲染的树(用户看到的内容)

<x-page>
  <header slot="header">header</header>
  <h1>my page title</h1>
  <p>my page content<p>
  <footer slot="footer">footer</footer>
  <div>some shadow content</div>
</x-page>

浏览器已经查看了 Shadow 主机的直接子元素(myXPage.children),并查看了其中是否有任何子元素具有与主机 shadowRoot 中的 <slot> 元素名称匹配的 slot 属性。

当找到匹配项时,该节点将视觉上“分发”到相应的 <slot> 元素的位置。在此匹配过程结束时,任何未分发的子元素都将分发到一个默认的(未命名的)<slot> 元素(如果存在)。

支持
  1. 分发更明确,更易于理解,更少“魔法”。
  2. 分发对于引擎来说更简单。
反对
  1. 没有解释内置元素(如 <select>)是如何工作的。
  2. 用 slot 属性装饰内容对用户来说是额外的负担。
  3. 表达能力较弱。

“closed” 与 “open”

shadowRoot 为“closed” 时,无法通过 myHost.shadowRoot 访问它。这使组件作者可以一定程度地确保用户不会窥视实现细节,类似于您可以使用闭包来保持私有性。

Apple 强烈认为这是一项重要功能,他们会对此进行阻碍。他们认为,实现细节永远不应该暴露给外部世界,并且在 “隔离的”自定义元素 成为现实时,“closed” 模式将成为一项必需的功能。

谷歌另一方面认为“封闭”的影子根将阻止一些可访问性和组件工具的使用案例。他们认为,意外地遇到shadowRoot是不可能的,如果人们想这样做,他们很可能有一个充分的理由。JS/DOM是开放的,让我们保持这种状态。

四月的会议上,很明显要继续前进,“模式”需要成为一个功能,但供应商正在努力就这是否应该默认为“打开”或“关闭”达成一致。因此,所有人都同意,对于V1,“模式”将是一个必需的参数,因此不需要指定默认值。

element.createShadowRoot({ mode: 'open' });
element.createShadowRoot({ mode: 'closed' });

穿透组合器

“穿透组合器”是一种特殊的CSS“组合器”,可以从外部世界定位影子根内部的元素。例如/deep/后来改名为>>>

.foo >>> div { color: red }

当Web组件首次被指定时,人们认为这些是必需的,但在查看它们的使用方式后,它似乎只带来了问题,使打破使Web组件如此有吸引力的样式边界变得太容易。

性能

如果引擎不必考虑任何外部选择器或状态,则在严格范围的Shadow DOM内部,样式计算可以非常快。穿透组合器本身的存在禁止了这种优化。

替代方案

放弃穿透组合器并不意味着用户将永远无法从外部自定义组件的外观。

CSS自定义属性(变量)

Firefox OS中,我们使用CSS自定义属性公开可以从外部定义(或覆盖)的特定样式属性。

外部(用户)

x-foo { --x-foo-border-radius: 10px; }

内部(作者)

.internal-part { border-radius: var(--x-foo-border-radius, 0); }
自定义伪元素

我们还看到一些供应商表达了重新引入定义自定义伪选择器的能力的兴趣,这将公开给定的内部部分以供样式化(类似于我们今天对<input type=”range”>的部分进行样式化)。

<span class="hljs-tag">x-foo</span><span class="hljs-pseudo">::my-internal-part</span> <span class="hljs-rules">{ <span class="hljs-rule"><span class="hljs-attribute">... }</span></span></span>

这很可能将在Shadow DOM V2规范中考虑。

Mixins – @extend

有一个建议的规范SASS的@extend行为引入CSS。这对组件作者来说将是一个有用的工具,允许用户提供一个属性“包”以应用于特定内部部分。

外部(用户)

<span class="hljs-class">.x-foo-part</span> <span class="hljs-rules">{
  <span class="hljs-rule"><span class="hljs-attribute">background-color</span>:<span class="hljs-value"> red</span></span>;
  <span class="hljs-rule"><span class="hljs-attribute">border-radius</span>:<span class="hljs-value"> <span class="hljs-number">4px</span></span></span>;
<span class="hljs-rule">}</span></span>

内部(作者)

<span class="hljs-class">.internal-part</span> <span class="hljs-rules">{
  <span class="hljs-rule">@<span class="hljs-attribute">extend .x-foo-part;
}</span></span></span>

多个影子根

为什么我需要在同一个元素上拥有多个影子根?我听到你问道。答案是:继承

让我们假设我正在编写一个<x-dialog>组件。在这个组件中,我编写了所有标记、样式和交互,以提供一个打开和关闭的对话框窗口。

<x-dialog>
  <h1>My title</h1>
  <p>Some details</p>
  <button>Cancel</button>
  <button>OK</button>
</x-dialog>

影子根通过<content>插入点将任何用户提供的内容拉入div.inner

<div class="outer">
  <div class="inner">
  <content></content>
  </div>
</div>

我还想创建一个<x-dialog-alert>,它看起来和行为与<x-dialog>一样,但具有更受限制的API,有点类似于alert('foo')

<x-dialog-alert>foo</x-dialog-alert>
var proto = Object.create(XDialog.prototype);

proto.createdCallback = function() {
  XDialog.prototype.createdCallback.call(this);
  this.createShadowRoot();
  this.shadowRoot.innerHTML = templateString;
};

document.registerElement('x-dialog-alert', { prototype: proto });

新组件将有自己的影子根,但它被设计为父类的影子根之上工作。<shadow>表示“较旧”的影子根,允许我们将其中的内容投影到里面。

<shadow>
  <h1>Alert</h1>
  <content></content>
  <button>OK</button>
</shadow>

一旦你理解了多个影子根,它们就成为一个强大的概念。缺点是它们带来了很多复杂性,并引入了很多边缘情况。

没有多个阴影的继承

即使没有多个影子根,继承仍然是可能的,但它涉及手动修改父类的影子根。


var proto = Object.create(XDialog.prototype);

proto.createdCallback = function() {
  XDialog.prototype.createdCallback.call(this);
  var inner = this.shadowRoot.querySelector('.inner');

  var h1 = document.createElement('h1');
  h1.textContent = 'Alert';
  inner.insertBefore(h1, inner.children[0]);

  var button = document.createElement('button');
  button.textContent = 'OK';
  inner.appendChild(button);

  ...
};

document.registerElement('x-dialog-alert', { prototype: proto });

这种方法的缺点是

  1. 没有那么优雅。
  2. 你的子组件依赖于父组件的实现细节。
  3. 如果父组件的影子根是“封闭”的,这将是不可能的,因为this.shadowRoot将是undefined

HTML导入

HTML导入提供了一种将一个.html文档中定义的所有资产导入到另一个文档范围内的能力。

<span class="tag"><link</span> <span class="atn">rel</span><span class="pun">=</span><span class="atv">"import"</span> <span class="atn">href</span><span class="pun">=</span><span class="atv">"/path/to/imports/stuff.html"</span><span class="tag">></span>

先前所述Mozilla当前不打算实现HTML导入。这部分是因为我们想看看ES6模块在发布另一种导入外部资产的方式之前如何展开,部分是因为我们不认为它们启用了很多目前无法实现的功能。

我们已经在Firefox OS中使用Web组件超过一年,并发现使用现有的模块语法(AMD或Common JS)来解析依赖关系树,注册元素,使用正常的<script>标签加载似乎足以完成工作。

HTML导入确实适合更简单/更具声明性的工作流,例如较旧的<element>Polymer的当前注册语法。

随着这种简单性的到来,来自社区的批评认为导入没有提供足够的控制来被认真地视为依赖关系管理解决方案。

在几个月前做出决定之前,Mozilla有一个在标志后面的工作实现,但在不完整的规范中遇到了困难。

它们会怎样?

Apple隔离的自定义元素提案利用HTML导入样式方法为自定义元素提供它们自己的文档范围:也许将来会有一席之地。

Mozilla,我们想探索如何将导入自定义元素定义与即将推出的ES6模块API对齐。如果/当它们出现并能够让开发人员做一些他们无法做到的事情时,我们准备实施。

总结

Web组件是一个典型的例子,说明了如今在浏览器中添加大型功能是多么困难。每个添加的API都将无限期地存在,并成为下一个障碍。

类似于将一个巨大的打结的线球拆开,添加更多,然后再次打结。这个结,我们的平台,不断变得更大、更复杂。

Web组件已经规划了三年多,但我们乐观地认为终点即将到来。所有主要供应商都已加入,充满热情,并投入了大量时间来帮助解决剩余问题。

让我们准备好将网络组件化!

更多

关于 Wilson Page

Mozilla的前端开发人员。

Wilson Page的更多文章…


40条评论

  1. Brian Di Palma

    “部分原因是我们认为它们没有实现很多目前无法实现的功能。”

    其他规范呢?它们启用了哪些目前无法实现的功能?我们可以构建哪些应用程序,而我们目前无法构建?也许Web组件没有被Polymer广泛采用仅仅是因为Web开发人员不需要它们?

    2015年6月9日 下午1:54

    1. cody lindley

      点头。说得很好,问题也很棒。由Polymer设想的Web组件,为那些想要获得类似元素的东西并使其正常工作的人提供了更好的开箱即用的HTML。或者,一个标签UI或轮播UI。除此之外,我不清楚这些相当复杂的提议(除了自定义元素)如何帮助那些多年来一直为Web应用程序构建组件的人。除了两组人之外,收益似乎很小。第一组是想要构建非常简单的静态网页并仅使用HTML来完成它的人。第二组是构建这些自定义元素的人,以便第一组不必深入学习CSS和JS。这两组人可以从WC和也许Polymer中获得重大收益。老实说,这很棒。没问题。希望一切顺利。但是,我们这些多年来一直构建组件化/模块化Web应用程序,将其划分为组件/小部件区域的人,认为其收益微不足道(就采用像Polymer这样的东西而言)。这项技术似乎并没有真正解决与构建Web应用程序相关的痛点(承认它有助于简单网站)。真正需要的是在自定义元素周围添加一些糖衣,然后看看我们是否真的可以从声明式编程中获取一些价值。就我个人而言,所有这些自定义HTML的东西最终都会导致命令式代码,因为所有声明式代码都会导致命令式代码,以有条理的方式完成工作。现在,我发表了很多意见。而真正重要的是社区做了什么。而到目前为止,它做得很少。这比我的想法更能说明问题。

      2015年6月9日 下午3:43

      1. Paul van Dam

        抽象和封装是任何环境中的良好实践,为什么它们在HTML/CSS中不会如此?仅仅因为它们简化了开发,并不意味着它们只对技能有限的人有用,正如你试图描绘的那样。

        2015年6月10日 上午2:28

        1. Wilson Page

          我的意思是,一个由高质量组件组成的生态系统将降低初学者进行 Web 应用开发的门槛。我假设组件的编写主要由中级/高级社区负责。我们还没有一个好的方案来引导新手进入 Web 应用的世界,我认为 Web 因此而受损。

          从静态网站到单页应用程序的跨越很大。有能力的程序员可能会继续使用 React、Ember、Angular 等进行开发;但让我们也为那些处于中间水平的人提供一些东西,同时改进我们所熟知和喜爱的平台:)

          2015 年 6 月 10 日 下午 2:46

          1. Wes Johnston

            我认为,说使用这些工具的人“没有能力”有点不诚实。网络几十年来一直在朝着这个方向发展(早在 Alex Russel 参与之前)。这是一种很好的编写方式。这是所有其他现有平台的操作方式。多年来,出现了框架来填充它的一部分,但这只是可扩展 Web 清单的后期部分。构建低级别的东西,以便开发人员可以进行实验(其中一些属于此,因为 Shadow DOM 很难伪造),在成熟后将高级的东西标准化。

            2015 年 6 月 10 日 下午 1:12

      2. Wilson Page

        组装 Web Components 规范的 API 的好处是它们是分离的。如果您不想直接使用基于 Web Components 的框架,您仍然可以从 Shadow DOM 等技术中获益。

        全局 CSS 命名空间对于开发人员或浏览器来说,在推理方面并不友好。Shadow DOM 可用于应用程序的特定部分,为您的代码库带来理智,同时提高引擎性能。

        完全沉浸其中,为您挑选和混合 API,或者完全忽略它们;选择权在您手中:)

        2015 年 6 月 10 日 下午 3:01

    2. Wilson Page

      我认为,Web Components 最令人兴奋的地方在于

      1. 互操作性:我们都可以使用共享组件,而与我们选择的 UI 框架无关。
      2. 降低创建高质量 Web 应用程序的门槛:通过对现成组件进行声明式组合。
      3. 暴露浏览器内部的第一阶段:框架只能走这么远。Web Components 是朝着暴露 API 的正确方向迈出的一步,这些 API 将赋予开发人员创建与内置元素一样好的元素的能力。

      关于“启用目前不可能实现的功能”

      1. Shadow DOM:为我们提供了框架无法(或难以)实现的样式隔离和标记封装。
      2. 自定义元素:(如上所述)提供了一种普遍互操作的方式来定义“组件”。

      Web Components 尚未被社区广泛采用的原因

      只有 Chrome 支持 API 的一个版本,而 Polyfill 在生产中并不合理。大型公司在生产中提供引人注目的 Web Components 示例,将为其他人效仿提供动力。

      2015 年 6 月 10 日 下午 2:37

      1. Kevin Lozandier

        你能详细说明“Polyfill 在生产中并不合理”吗?显然,考虑到 Polyfill 的适应性和复杂性,每个人都有自己的体会。

        因此,笼统地说“Polyfill 在生产中并不合理”似乎有点片面。

        你是指过去特定的 Polyfill,现有的 Polyfill,还是真的想说 Polyfill 一般来说在生产中并不合理?

        2015 年 6 月 10 日 上午 11:16

        1. Kevin Lozandier

          *你能详细说明一下你说“Polyfill 在生产中并不合理”是什么意思吗?*

          2015 年 6 月 10 日 上午 11:17

        2. Wilson Page

          我的潜意识里,我想到的是 Shadow DOM Polyfill 不适合性能优异的移动 Web 应用程序。

          话虽如此,新的“Shady DOM”Polyfill 可能会[1]改变现状。我知道 Polymer 团队正在努力解决这个问题。

          [1] https://www.polymer-project.org/1.0/articles/shadydom.html

          2015 年 6 月 10 日 上午 11:27

  2. Joe

    也许这团乱麻需要更进一步地解开。以 React.js 和 JSX 为例,它提供了组件化的所有目标,并且可以很好地与非浏览器 DOM 目标配合使用。就我个人而言,我宁愿看到 XML 字面量添加到 ECMAScript 中,而不是将 Web Components 添加到 HTML 中。

    2015 年 6 月 9 日 下午 2:39

    1. Angus

      这是一个好主意。我们可以称之为“ECMAScript for XML”,或者简称为 e4x。

      2015 年 7 月 6 日 上午 9:45

      1. Michael J. Ryan

        你提到 e4x 很有意思,我真的很喜欢它很长一段时间……在 AS3 中广泛使用它,用于 Flash 与 VB.Net 后端的通信,VB.Net 后端也支持 XML 字面量……效果很好。现在我无处不在使用 JSON。

        不过,除了 Mozilla 之外,没有人对实现 e4x 感兴趣,v8 团队断然拒绝了这个想法(尽管该问题上有许多关注)。当然,JSX 作为计算模板,比使用 e4x 速度快得多,或者就渲染而言,比使用 ES6 模板处理速度快得多。我真的很喜欢 React,虽然我认为像 Polymer 这样的东西可能是未来,但我可能还会在新项目中坚持使用 React/Flux 工作流一段时间。

        2015 年 7 月 8 日 晚上 8:46

  3. Ron Waldon

    很棒的文章。感谢分享。

    2015 年 6 月 9 日 下午 5:22

  4. Kevin Lozandier

    嗨,Joe

    没有什么能阻止你用 React.js 或 JSX 创建 Web Components——无论是使用 Maple.js 这样的工具,还是使用 Polymer 这样的抽象。

    作为抽象,它希望您能了解 Web Components 当前的状态,这篇文章事实上可以帮助您理解这一点。

    Maple.js(它使用 Polymer 的 Polyfill 并拥有受 Polymer 启发的自己的抽象)和 Polymer 都是一种抽象,旨在帮助开发人员在今天创建 Web Components,这与 jQuery 多年前帮助开发人员忽略困扰 Web 的 DOM 不一致性类似。

    这些有争议的部分是可以争论的(例如,说“一般”的想法是“赘瘤”似乎有点不诚实),与此同时,这些 Web Components 抽象允许您使用 Web Components。

    Web Components 提供了迫切需要的能力,以提供更人性化的组件使用者体验

    在 2015 年,越来越多的开发人员或任何使用者不应关心您使用 ReactJSX 或 Angular 来创建组件,他们不应被迫记住添加 2-3 个不同的脚本才能使用它,也不应该记住复制粘贴没有任何语义意义的标记(divitis)才能在页面上拥有一个像轮播这样的组件。

    有了 HTML Imports、Shadow DOM、Custom Elements 和 Templates,我们不再有这些问题。

    2015 年 6 月 9 日 下午 5:45

  5. Sean Hogan

    自定义浏览上下文将有助于解决 Custom Elements 的问题,因此,自然地,YAGNI、NIH、TLN。

    Custom Element 注册只能在自定义浏览上下文安装期间发生,因此浏览器知道,如果浏览上下文已准备好,则 Custom Element 定义也已准备好。

    自定义浏览上下文可以(必须)在获取的页面进入视图之前截取其 DOM。这允许在页面进入视图**之前**将页面中的原生 HTML 元素替换为自定义元素,这比 @is 更灵活,例如:
    替换所有“
    为“

    自定义浏览上下文不支持 document.write(),因此这是一个已解决的问题。

    2015 年 6 月 9 日 晚上 8:26

  6. SteveLee

    很棒的文章和总结——谢谢

    如果 Polymer 只是一个跨浏览器讨论的参考实现,那将是件好事——然而,它变成了一个“万物皆组件”的框架。这完全是另一回事,也可能解释了浏览器采用率低的原因。

    我**非常**担心如何确保 Web Components 的渐进增强。我们需要为旧版浏览器、Opera Mina 或发生错误时提供安全机制。这很难实现,尤其是在依赖 JavaScript 的 Polyfill 的情况下。即使所有浏览器以支持 PE 的方式实现它们,这也取决于作者编写好的组件,这就像在 CMS 中提供用户提供内容的良好可访问性一样。

    2015 年 6 月 10 日 下午 3:38

    1. Kevin Lozandier

      在渐进增强方面,与所有依赖 JS 的事情一样,如果您想支持无 JS 环境,创建不带 JS 铃铛和口哨的组件“等级 1”版本是推荐的做法。

      Scott Jehl 有一本非凡的书籍可以帮助开发人员更好地理解这一点,叫做*负责的响应式设计*。

      在像 a11y 这样的事情上,Web Components 当然可以被那些不在意的开发人员随意实现。但这并不奇怪,因为渐进增强经常被忽略,因为开发人员不方便。

      幸运的是,Polymer——一个流行的 Web Component 抽象,最近第一个认真努力成为一个真正可以在生产中使用的 Web Component 库,它在上个月发布了 1.0 版本——以及它背后的团队付出了巨大的努力,提供抽象,使渐进增强变得容易*并推广广泛的 a11y 最佳实践*。

      以下幻灯片是来自 Google IO 的关于后者的一个很好的资源:https://#/3WsvkqQWUR

      此外,这篇最新文章提供了在今天进行 PE Web Components 的常见方法,以及您可以为 Polymer 的性能而做的一些简单的事情:https://aerotwist.com/blog/polymer-for-the-performance-obsessed/

      Polymer 1.0 甚至附带了完全专用于这些 a11y 的元素、类型扩展和行为混合。

      我认为,这些问题是合理的,但随着像 Polymer 这样的生产就绪的 Web Component 抽象越来越努力地解决这个问题,这些问题得到了很大程度的缓解——特别是为了让开发人员因为 PE 问题而忽略 Web Components 的借口越来越少。

      `is` 的弃用使事情变得有点复杂,直到标准机构真正提供一个我们可以今天使用的更高级的替代方案。

      总的来说,我希望这些信息能帮助您减少对确保 Web Components 的渐进增强的担忧。

      2015 年 6 月 15 日 上午 9:30

      1. SteveLee

        Kevin——感谢你如此深思熟虑的回答。斯科特的书在我的阅读清单上——我应该把它排到前面。我已经相信 PE、响应式和可访问性都是同一个拼图的一部分。

        我不知道 Polymer 支持 PE,我做了很多搜索也没找到。我印象中它假设客户端扩展基本 Web 功能才能工作(JavaScript、AJAX 等)。我还没有探索过 1.0 版本,感谢你的指点。我决定使用 jQueryMobile 作为 PE 基础,然后边走边构建组件。

        我想使用 Web Components 的一个原因是,Mozilla AppMaker 提供了一些真正的希望,可以使用 Bricks(建立在 Web Components 之上的抽象,添加了一个消息层)来支持不太专业的应用程序创建。

        支持 PE 的一个重要部分是提供一个服务器渲染的传统 Web 体验,其中与服务器通信的唯一方式是链接和表单。这需要额外的工作(以及同构声称解决的明显重复)。

        然而,考虑到为 Web 开发意味着为一个分布式、不可靠的系统开发,其中用户偏好、设备和环境千差万别,所以付出额外的努力是合理的。并非每个人都能用最新版本的常青浏览器进行快速可靠的连接。这就是 Web 开发的“乐趣”。

        再次感谢 :)

        2015 年 6 月 16 日 下午 2:58

    2. Kevin Lozandier

      你能解释一下,你所说的 Polymer 变成“一切都是组件”的框架是什么意思?

      从表面上看,它只提供了一个抽象层来创建 Web 组件;组件的用户或开发人员可以决定他们希望以这种构建 Web 应用程序的方式进行多大的范式转变。

      有些人可能仅仅是将他们现有的组件重新创建为更可复用的组件,以便在他们的应用程序中使用,现在也使它们更容易被用户在应用程序之外使用。

      其他人可能会采取更大胆的做法,将元素组合起来,使其功能与单页面 JS 框架相媲美。

      另一个极端是,人们将元素组合起来,使他们应用程序的单个实例可以通过“。”实例化。

      尽管如此,后者只是一个极端的例子,对于他们的用例来说,很有可能是合理的。无论如何,Polymer 不会要求你,也不会强迫你认为“一切都是组件”。

      充其量,他们提供了一个很棒的入门套件,展示了元素如何很好地协同工作,并使用最少的与组件无关的 JS 来提供 SPA 体验。

      考虑到即使那不是一种“一切都是组件”的方法,我想知道这种思维方式的例子。以便更好地评估这种想法的优缺点。

      2015 年 6 月 15 日 上午 10:20

      1. Kevin Lozandier

        有趣的是,这个评论线程中没有包含任何看起来像标签的东西;似乎是可以用更优雅的方式处理的事情。

        让我重新措辞一下这段话

        另一个极端是,人们将元素组合起来,使他们的应用程序的单个实例可以通过 body 元素内的单个组件实例化。

        2015 年 6 月 15 日 上午 10:25

      2. SteveLee

        > 你能解释一下,你所说的 Polymer 变成“一切都是组件”的框架是什么意思?

        Kevin - 从获得关于 W3C 关于 Web 组件的概念的反馈的角度来看,比如非 UI/DOM 标记组件,比如计时器,或者是你提到的那种极端的例子,比如用于顶级的标签,这似乎有点偏离主题了。

        我同意,应该有选择权,你如何使用添加自定义元素的能力,出于任何原因。你可以把它看作是一种声明式 DSL 方法。我个人倾向于将 Web 组件作为 UI 元素,并使用其他工具来构建架构组件

        2015 年 6 月 16 日 下午 2:38

  7. Evan You

    我很遗憾地看到,规范的状态比我想象的要更加分歧。但如果你想寻找一种类似 Web 组件的开发解决方案,而不用担心规范的变更或 polyfill,你可以看看 Vue.js: https://vuejs.net.cn/guide/components.html

    2015 年 6 月 10 日 上午 7:48

  8. Cameron Spear

    互操作性是一个巨大的问题。PHP 社区正在推动创建与框架无关的组件,这些组件可以轻松地集成到任何(现代)框架中。

    对于 JavaScript 来说,情况也是如此,但如果你没有 React/Angular/Ember 的支持,那么你所能做的就非常有限了,而 Web 组件可以帮助弥合这一差距。我们将看到更多复杂的组件,比如日期选择器和带搜索功能的选择器等等,它们有可能与“大型框架”很好地配合使用,比今天更胜一筹。

    2015 年 6 月 10 日 下午 7:00

    1. Evan You

      我并不是说规范本身就是一个坏主意。问题有两个:人们想要面向组件的开发体验,以及互操作性。目前,Web 组件试图解决这两个问题,但事实证明,它本身并不能很好地解决开发体验问题。IMO,规范最好只专注于互操作性,而将开发体验部分留给框架来解决。

      p.s. 将 Vue 组件包装成一个符合规范的自定义元素非常简单:https://github.com/vuejs/vue-element

      2015 年 6 月 15 日 上午 10:14

  9. Thad

    ‘DMITRY’ 是指什么?

    2015 年 6 月 11 日 上午 7:57

    1. Wilson Page

      它只是提出这个建议的人的名字。

      2015 年 6 月 28 日 下午 9:20

  10. markg

    关于多个阴影根,我的理解是,已经决定在 v1 Web 组件中不会包含多个阴影根。对吗?

    2015 年 6 月 11 日 下午 9:08

    1. Wilson Page

      没错。

      2015 年 6 月 12 日 上午 0:38

  11. Luke

    我很惊讶,自定义元素在高性能 Web 组件中得到了如此多的考虑。在我看来,添加所有这些功能会使 DOM 比现在更慢,而像 Backbone 这样的框架可以让你将模型从 HTML DOM 中抽象出来,将来,每个人可能会使用对象来生成 canvas 或 famo.us 平台,而不是传统的 HTML DOM 对象。考虑到像 Backbone 这样的框架和模板,我认为总体趋势是反对在元素中包含语义标记和数据,而是使用 JS 模型/视图/对象来根据需要渲染视图(无论是 DOM 元素还是 canvas、svg 或 famo.us 等的一部分)。

    我很好奇,这里提到的任何 Web 组件实现是否会放宽对拥有一个主要 UI“线程”(即不在 Web 工作者中的 JS)能够访问 DOM 的限制。例如,Java Swing 有 SwingWorker,它能够从线程中访问 UI,而 Android 有一个叫做 runONUIThread(runnable…) 的东西。对于可能减慢浏览器速度的 DOM 密集操作,是否会有类似的东西?

    2015 年 6 月 11 日 下午 10:50

    1. Stephen Williams

      runOnUIThread() 只是将请求放入队列中,以便在主 UI 线程空闲时执行。SwingWorker 可能正在做类似的事情,确保只有一条线程在同一时间访问 GUI 资源。

      2015 年 7 月 9 日 上午 0:48

  12. Erik Isaksen

    这是我迄今为止读过的关于实现的最好的文章。读到一篇细节如此丰富且直截了当的文章真是令人耳目一新。

    2015 年 6 月 12 日 下午 1:32

    1. Wilson Page

      哇,感谢你的赞美之词,Erik :)

      2015 年 6 月 15 日 上午 0:45

    2. Kristian Gerardsson

      同意

      2015 年 6 月 15 日 上午 5:10

  13. Neil Stansbury

    哦,对于 XBL2……

    Web 组件绝对值得努力——Firefox 中的 XBL 使我确信了这个概念的巨大价值。

    对我来说,`