31

作为多年来一直试图通过创建 (HTML) 组件来帮助内容作者开发和维护大型网站的人,我真的很高兴看到 Web 组件在 w3c、google 和 mozilla 中获得关注。但在我看来,规范中没有针对 javascript 库膨胀的措施。

假设我开发A的组件依赖于underscore.js并想要使用组件BC并且依赖于lodash.js版本 1.* 等。
我看不到任何标记依赖项和库版本的方法。当我们谈论具有多个团队和利益相关者的网站时,这可能会导致图书馆的巨大膨胀。

当前的解决方案是在全球范围内对整个网站的批发客户端框架进行标准化。当您在不同的服务器端框架(如LifeRay(java)、EpiServer(.net)、Django(python)等)中投入大量资源时,这很困难,每个框架都有首选的客户端库。

我认为 Web 组件是一种将服务器端框架与客户端代码分离的手段,但客户端依赖处理的遗漏令人担忧。

是在规范中但我错过了它,还是有缓解这个问题的策略,我不知道?

[提到的图书馆只是例子。问题与框架、库和服务器端语言无关]

更新 感谢大家的回答。我很惊讶没有人提到最近大肆宣传的Mozilla X-TagGoogle Polymer 。我完全接受了影子 DOM、范围样式、自定义元素等的想法,但我没有看到任何关于如何处理 JavaScript 依赖项的提及。正如@Daniel-Baulig 正确编写的那样,HTML Imports根本没有提到 JavaScript。我承认这个问题几乎不可能回答。然而,当@Daniel-Bailig 提到 ES6 模块时,我认为他是最接近的。我个人认为我们会在 ES6 Modules 和 require.js 之间找到一个可持续的解决方案。

4

8 回答 8

6

这也是困扰我一段时间的问题,尤其是在面对维护许多开发人员触及的代码时。您经常会遇到一个解决方案中包含多个 JS 库(其中一些本质上做同样的事情),更不用说一个解决方案中使用的同一框架的不同版本。

我正在寻找的或者更确切地说是“一个”潜在的解决方案是创建一种中介框架。

基本思想是“反对”中介编码(从不直接访问/使用 js 库,而是通过中介使用它),从而基本上使代码不可知(与其“父”库分离)并在下面包含中介实现.

这不会解决我/我们的直接问题或臃肿,但我编写的任何 Web 组件都可以跨框架运行。

这是一个概念证明: Tagger Mediator POC

例如,调解员包括:

jQuery (1.9.1)

Mootools (1.4.5)

原型 (1.7.1.0)

小唯 (3.10.3)

道场 (1.9.1)

分机 (3.4.0)

泽普托 (1.0)

但是没有什么能阻止任何人创建他们自己的中介框架,它“抽象”了其他中介,嗯,所以可能也会导致膨胀(让事情变得更糟而不是更好)。

我想这取决于你设定自己的标准;)

于 2013-12-04T13:19:28.300 回答
5

在当前的W3C 规范中,似乎没有定义依赖关系甚至版本它们的特定方法。组件不应使用任何库/依赖项或与它们紧密耦合。

这意味着每个主要库都可能会带来他们自己的一组组件,这些组件期望这些库已经被加载。

也许ES6 模块在这方面提供了帮助,但话说回来,它们目前也不提供任何版本控制机制。

这一切都表明该规范处于相当早期的阶段,并且可能会发生变化。向规范作者提出依赖关系问题可能会将该主题带到桌面上,甚至可能在规范固化之前解决。最后,无论平台和语言如何,使用不同的库在单个代码库中执行相同的任务一直是并将继续成为软件开发中的一个问题。您只需要就在您的代码库中使用哪些框架/库达成一致,即使这意味着将您与其他人隔离开来。

此外,如果您今天已经对为 Web 开发独立组件感兴趣,您可能想看看React 库

于 2013-12-04T13:45:55.500 回答
3

我自己也对 Web 组件有过类似的疑虑,但后来我开始使用第三方 React 组件。他们有同样的问题,因为他们带来了他们自己的依赖。但更糟糕的是,因为 React 不喜欢与其他版本的 React 一起使用,所以 React 组件必须将 React 列为 Peer Dependency。这意味着您被迫使用与第三方组件相同版本的 React。

所以我的结论是,这实际上是web 组件的一个特性!每个自定义元素都可以拥有自己的依赖项,完全独立于应用程序的其余部分。我认为第三方组件是 Web Components 的主要用例;消费者希望能够使用摩擦力尽可能小的组件。当然会有一些重复的代码,但组件消费者不需要知道或关心。我认为组件作者将通过在其组件中使用更小的模块来解决这个问题。例如,他们可以使用 Snabbdom 之类的东西,而不是使用 React。

如果您正在使用您控制的 Web 组件构建整个应用程序,您仍然可以使用 Browserify 或 WebPack 之类的捆绑工具来管理您的依赖项。这些将允许您覆盖某些模块,以便您甚至可以强制第三方组件使用与您的应用程序的其余部分相同的版本(请参阅:https ://www.npmjs.com/package/aliasify )。这可能会破坏一些模块,但这就是生活。

于 2016-09-23T21:56:00.740 回答
2

我从未听说过标准化 javascript 框架。然而,对于 HTML5,一些在早期版本的 HTML 中过去需要 javascript 框架的部分现在已作为标准功能添加到 HTML5(例如标签<canvas>、圆角边框、阴影效果......),这是解决问题的第一步你在说什么。

老实说,我认为这永远不会发生,或者至少不会在不久的将来发生。所有这些框架都有自己的目的、优点和缺点。直到你可以构建一个巨大的 javascript 库,以某种方式将它们结合起来,网络上总会有不同的库使用。

另一个重要的一点是不同库之间的竞争,它使 javascript 市场不断增长并提出新的创新。如果您要创建一个所有人都可以使用的标准 javascript 库,那么您还将消除不同框架之间的竞争,从而保持创新和进步。

于 2013-12-04T11:37:09.837 回答
2

库膨胀不一定通过使用 Web 组件来缓解,事实上您可以通过使用自定义构建来减少库(链接是 Lo-Dash,但其他流行的库有构建步骤)。这里的某种自动化可能非常有用,即一种可以扫描您的源代码并根据您使用的功能生成自定义构建的工具。

我认为随着npm的兴起,此类库变得越来越不重要。Lo-Dash 就是一个很好的例子,因为它的功能作为独立模块在 npm 上发布,但你也有像Sizzle这样的东西,jQuery 使用的 CSS 选择器引擎。如果你足够努力,你会发现很多插件都是在没有jQuery 作为依赖项的情况下编写的,或者它在项目的路线图中删除依赖项,或者有人已经分叉了项目以删除它对另一个库的依赖。例如,Exoskeleton,一个完全下划线和 jQuery 免费版的 Backbone。

我不认为我们会看到另一个流行的实用程序库,例如 jQuery 或 underscore。使用 npm,我们可以简单地选择我们想要的模块,并派生依赖于这些大型库的项目,以仅使用他们需要的模块或完全无依赖的版本来重写它们。

使用 AMD 和requirejs,这已经成为现实。您可以定义一些源代码的依赖项;而不是链接到单体库,在你的代码中你可以声明这个组件只需要例如microajax而不是整个 jQuery 库:

define(['microajax'], function(microAjax) {
    microAjax("/resource/url", function (res) {
      alert (res);
    });
});

查看microjs 网站,了解更多类似这样的帮助库。

就 Web 组件而言,我希望这些组件的作者以这样一种方式编写他们的组件,这样他们就不需要像 jQuery 这样可以完成所有工作的大型库。如果他们这样做,我也希望我可以分叉它们并自己修剪所有不必要的部分。:-)

编辑:这篇 24ways 文章很好地介绍了原生 JS 特性的兴起,这些特性最终将取代 jQuery。值得一提的是,jQuery 是在 JavaScript 实现完全不同的时代编写的。但随着标准化的提高和 API 变得更加一致,对原生功能的包装器的需求有所减少。例如,现在我们有querySelectorAll

// jQuery
$('.counter').each(function (index) {
  $(this).text(index + 1);
});

// Vanilla JavaScript
var counters = document.querySelectorAll('.counter');
[].slice.call(counters).forEach(function (elem, index) {
  elem.textContent = index + 1;
});
于 2013-12-10T22:27:16.753 回答
2

恕我直言,您描述的问题始终存在于前端,与 Web 组件没有直接关系,但 Web 组件确实使情况变得更糟

假设如果你依赖组件 A 和 B,它们都依赖于 lodash,除非 A 和 B 都将 lodash 设置为对等依赖项(这使得它们看起来像大多数 jquery 插件一样的插件),那么你可以包含一份 lodash,否则, A 和 B 对你来说就像黑匣子。尽管捆绑器可能会进行一些构建后处理,例如 tree-shaking/dead code 消除来帮助缓解这种情况,但这永远不会 100% 起作用,例如 A 和 B 都依赖于两个不同版本的 lodash。

为什么 Web 组件会使情况变得更糟?WC 的目标是提供一种与框架无关的方式来重用 UI 组件,例如,如果我使用 vue 和一堆 vue 组件,我捆绑的 js 最终会得到这些组件,并且单个 vue 运行时因为 vue 是这些组件中的对等 dep . 现在,如果我使用 WC 和一堆 WC 组件,我很可能最终会得到一个更大的捆绑 js。例如,可以用一个简单的按钮创建一个简单的 lit-element WC 组件,然后构建项目,看看 bundle js 是如何变大的。有些框架可能采用不同的方法,例如 vue,仍然需要您将 vue 添加为外部框架来运行基于 vue 的 WC,如果您将 vue 切换到 WC,这不会为您节省一个字节。此外,就减少包大小而言,共享样式是 WC 中的另一个问题。

我的公司使用引导程序和反应,我们的应用程序捆绑了 1 个引导程序、1 个反应和许多外部反应组件,最终已经是 3.5mb js/css,我怀疑我们可以在 3.5mb 的 WC 中做同样的事情。

于 2019-05-22T12:54:04.847 回答
1

假设我开发了对 underscore.js 具有依赖关系的组件 A,并且想要使用对 lodash.js 版本 1.* 等具有依赖关系的组件 B 和 C。我看不到任何标记依赖关系和库版本的方法。

有一个 1999 年的旧 ECMA 规范(ECMA-290)指定了一个组件机制,其中包括依赖项和库版本的元素和属性:

<component
name="com.mycompany.selectnav"
displayname="SelectNav"
src="selectnav.js"
env="client"
hint="Navigational Select List"
version="1.0"
needsform="yes">
</component>

对于依赖项,请使用customizer元素。对于版本控制,请使用该meta元素。例如:

<customizer type="ecmascript" url="http://com.com/foo">
  <meta name="version" value="1.1b"/>
</customizer>

现代实现的 JSON 编码如下所示:

{
"component":
 {
 "name":"com.mycompany.selectnav",
 "displayname":"SelectNav",
 "src":"selectnav.js",
 "env":"client",
 "hint":"Navigational Select List",
 "version":"1.0",
 "needsform":"yes",
 "customizer":
   {
   "type":"ecmascript",
   "url":"http://com.com/foo",
   "meta":
     {
     "name":"version",
     "value":"1.1b"
     }
   }
 }
}

与CDN APIAPI 检查器按需脚本加载器集成的生命周期回调将是另一种选择:

createdCallback => check API URL => createElement for dependent script tags => onload event for dependent script tags => appendChild for dependent script tags

在以下项目中对 HTML 进行子集化是一种已尝试过多次的解决方案:

参考

于 2014-01-27T23:05:09.413 回答
1

我们只是尝试将我们的 Web 组件 javascript 保持在最低限度,将它们的占用空间保持在非常低的水平。因此,我们不使用下划线,而是使用原生的 ES6 等价物(考虑到现有的使用 Web 组件,这并没有太大的延伸)。

然后我们用一个 html 文件、一个 css 文件和一个 js 文件打包我们的 web 组件。它们实际上可以由多个文件组成,但我们的构建过程会处理这些。我们的 javascript 文件与 Browserify 捆绑在一起,允许我们使用 npm 模块,同时将它们很好地打包在我们的 Web 应用程序中。

这提供了非常漂亮的微小但黑色的 Web 组件,可以共享通用模块而不会发生任何冲突,并且不必担心 AMD 开销,只需简单的 commonjs 即可。

如果我们确实需要跨多个组件捆绑一个重型库,我们要么将该库设为外部并将其传入,而将其包含在应用程序中(由于主干,这就是您必须使用主干.js 执行此操作的方式。 js 使用 instanceof 而不是鸭子类型,使多个backbone.js 实例无法相互通信),或者让 web 组件使用像 wzrd.in 这样的 browserify cdn 服务器将其捆绑在一起——但是,对于父 web 应用程序来说,处理起来要好得多大型部门,好像Web组件使用不同的cdns,那么您就有问题了。

于 2014-04-14T01:23:02.240 回答