0.0.2 • Published 2 years ago

@web-widget/web-component-modules v0.0.2

Weekly downloads
-
License
-
Repository
github
Last release
2 years ago

Web Component 模块系统

目标

为 Web Component 设计一个模块系统,使 Web Component 更好的实现良好的组件化,以便集成在现有的 ES module 的基础设施中,并且能够在当今的浏览器中立即能工作。

动机

ES module 的引入为 JavaScript 开发人员提供了一些好处,包括更多的组件化代码和更好的依赖性管理。然而,自定义元素的 JavaScript 脚本与 HTML 标签之间没有任何显著的联系,这些不利于静态分析工具进行检查,在一些诸如无代码、低代码的系统中也会增加依赖管理的复杂度。

提议内容

在自定义元素上定义一个名称为 is="web-component-module" 属性来表示为 Web Component Modules,这样加载器可以根据 src 或者 import 属性来载入自定义元素的 Class、完成自定元素注册等流程。

<my-element is="web-component-module" src="./index.js"></my-element>
<hello-world is="web-component-module" import="@org/ui-widget"></hello-world>

这样我们将得到明显好处:

  • 组件的标签可以自己管理依赖的 JavaScript,删除标签后也意味着删除了 JavaScript 依赖,这有利于一些可视化的网页编辑器进行操作
  • 收敛了组件使用 customElements.define 的副作用,使得组件的使用者真正拥有了标签命名权。例如可以预先定义 <ui-dialog> 这样的通用的组件抽象,然后交给外部的 Web Component UI 库去实现这个标签,这样完成了和具体的 Web Component UI 库的实现解耦,让工程能够适应长久的变化

替代方案对比

目前 W3C 有两个关于解决此问题的相关提案,分别是:

  • html-imports 是独立于 ES6 模块开发的,目前已经被废弃
  • html-modules-explainer 它设计了一个更好的与 ES module 结合的提案,但目前还没有浏览器或者开发生态支持。它和 Web Component Modules 提案并不冲突,但 Web Component Modules 是为了简化依赖关系管理,并且确保能够立即在生产环境使用

指引和例子

安装加载器

npm install @web-widget/web-component-module

使用

import { WebComponentModule } from '@web-widget/web-component-module';
WebComponentModule.update(document);

裸模块

import maps 这样的标准将允许浏览器理解这些类型的导入,而无需转换步骤,这使得我们可以在浏览器中实现版本管理与共享依赖,而不需要 package.json 文件与构建工具。

<script type="importmap">
{
  "imports": {
    "@org/app": "https://cdn.jsdelivr.net/npm/@org/app/dist/esm/main.js"
  }
}
</script>

<my-element is="web-component-module" import="@org/app"></my-element>

importsrc 属性的不同:import 属性不会自动补全路径,加载器会优先读取它的原始值去加载模块,因此它能够支持裸模块。

支持 SystemJS

system 格式被设计为 esm 的过渡格式,它解决了 esm import maps 浏览器兼容性的问题。由于几乎所有的构建工具都支持输出 system 格式,因此我们推荐在生产环境中使用它,以便未来能够无缝过渡到 Web 标准。

通过插件可以支持 ES module 以外的格式,例如下述代码演示了如何支持 SystemJS 的格式:

import 'systemjs/s.js';
import { WebComponentModule } from '@web-widget/web-component-module';

WebComponentModule.loaders.define('system', async options => {
  const nameOrPath = options.import || options.src;
  if (!nameOrPath) {
    throw new Error(`No 'src' or 'import' attributes were found`);
  }

  return System.import(/* webpackIgnore: true */ nameOrPath).then(
    module => module.default
  );
});

WebComponentModule.update(document);
<my-element is="web-component-module" type="system" src="./index.js"></my-element>

使用裸模块:

<script type="systemjs-importmap">
{
  "imports": {
    "@org/app": "https://cdn.jsdelivr.net/npm/@org/app/dist/system/main.js"
  }
}
</script>

<my-element is="web-component-module" type="system" import="@org/app"></my-element>