发布于 2025-12-10 4 阅读
0

为什么我为 Web Components 编写了一个微型库 挑战自己 摆脱痛苦 完全控制 分发 装饰器 输入 Readymade 示例 Readymade 现在是 v1

我为什么要为 Web Components 编写一个微型库

挑战自己

消除疼痛

完全控制

分配

装饰器

进入 Readymade

一个例子

Readymade 现已升级至 v1

我知道似乎每个人都在构建这个微型计算机,那个微型计算机。

微服务、微前端,现在还有微库?!

目前已经有了开发 Web 组件的优秀解决方案。

一些主流的 JavaScript 框架,例如 Svelte 和 Angular,甚至可以编译为自定义元素。不过,考虑到将现代 JavaScript 框架编译为 Web 组件所需的工具数量,这可能有点过头了。

那么我为什么要编写另一个库呢?

挑战自己

构建一个现代的、零依赖的框架。我想要一个只使用浏览器中 API 的解决方案。这意味着某些功能需要 polyfill,但这没问题。事实证明,浏览器中存在一些 API,可以让你构建一个支持数据绑定、高级事件处理、动画等功能的 UI 微型库!

  • 自定义元素
  • 创建TreeWalker
  • 代理人
  • 自定义事件
  • 广播频道
  • 网络动画

消除疼痛

开发 Web 组件是该项目的另一个目标。自定义元素的编码涉及大量可精简的样板代码。在允许ShadowDOM和不允许的自定义元素之间切换可能很困难。自主自定义元素的处理方式与自定义内置元素不同。事件处理仅与典型的 DOM 一样好,需要调用addEventListenerdispatchEvent,即使这样,您也只能遵循事件通常冒泡的方式。此外,更新自定义元素的模板也存在问题,需要选择 DOM 并更新属性和内部内容。这为工程师提供了做出性能不佳的选择的机会。如果一个库可以处理所有这些,那会怎样?

完全控制

这正是我想要的。如果我想改变这个库的行为方式,我可以做到。Readymade 可以将其构建为开箱即用的 SVG 支持(它确实如此),但如果我想支持 GL 对象,它也可以渲染。只需更换状态引擎,WebGL 支持就来了。我一直在尝试不同的 UI,需要一个可塑性强的 UI。

分配

这是我长期以来一直在做的另一个项目的关键方面。我希望找到一种不依赖任何框架的方法来分发 UI 组件库。该项目的目标是提供一个小于 20KB 的 UI 库。Readymade 本身的大小约为 3KB,导入了所有必要的功能。使用 Readymade 构建的组件可以像任何其他 DOM 元素一样,在任何使用 JavaScript 框架构建的项目中使用,前提是该框架支持自定义元素。

装饰器

在 Angular 中,这些功能对我来说是理所当然的,我想了解这些高阶函数是如何工作的。我构建的微库高度依赖于这个未来的规范,但这也没问题。使用 TypeScript 从头构建这个库还提供了类型检查、IntelliSense 等额外优势,并且让我能够使用优秀的 TypeScript 编译器。

进入 Readymade

Readymade 是一个用于处理 Web 组件开发常见任务的微型库。其 API 与 Angular 或 Stencil 类似,但内部机制有所不同。Readymade 使用上面列出的浏览器 API,为您提供丰富的开发体验。

  • 🎰 声明 CSS 和 HTML ShadowDOM 模板的元数据
  • ☕️“自主定制”和“定制内置”元素的单一界面
  • 🏋️‍ ‘Hello World’(gzip 压缩)大小约为 1Kb
  • 1️⃣ 单向数据绑定
  • 🎤 事件发射器模式
  • 🌲 可摇树

一个例子

下面的按钮示例展示了 Readymade 的一些优势。


import { ButtonComponent, Component, Emitter, Listen } from '@readymade/core';

@Component({
    template:`
    <span>{{buttonCopy}}</span>
    `,
    style:`
        :host {
            background: rgba(24, 24, 24, 1);
            cursor: pointer;
            color: white;
            font-weight: 400;
        }
    `,
})
class MyButtonComponent extends ButtonComponent {
    constructor() {
        super();
    }
    @State() 
    getState() {
      return {
        buttonCopy: 'Click'
      }
    } 
    @Emitter('bang')
    @Listen('click')
    public onClick(event) {
        this.emitter.broadcast('bang');
    }
    @Listen('keyup')
    public onKeyUp(event) {
        if (event.key === 'Enter') {
            this.emitter.broadcast('bang');
        }
    }
}

customElements.define('my-button', MyButtonComponent, { extends: 'button'});

Enter fullscreen mode Exit fullscreen mode
  • ButtonComponent是一个预定义的 ES2015 类,它扩展HTMLButtonElement并链接了一些支持装饰器中定义的函数templatestyleComponent调用其他装饰器添加到该类原型的任何方法。这里有趣的部分是它ButtonComponent是可组合的。以下是其定义。
export class ButtonComponent extends HTMLButtonElement {
  public emitter: EventDispatcher;
  public elementMeta: ElementMeta;
  constructor() {
    super();
    attachDOM(this);
    attachStyle(this);
    if (this.bindEmitters) { this.bindEmitters(); }
    if (this.bindListeners) { this.bindListeners(); }
    if (this.onInit) { this.onInit(); }
  }
  public onInit?(): void;
  public bindEmitters?(): void;
  public bindListeners?(): void; public bindState?(): void;
  public setState?(property: string, model: any): void;
  public onDestroy?(): void;
}
Enter fullscreen mode Exit fullscreen mode
  • State允许您为按钮实例定义本地状态,并且状态中定义的任何属性都可以绑定到模板。Readymade 在底层使用document.createTreeWalkerProxy来监视更改并独立地更新attributestextContent

  • Emitter定义一个 EventEmitter 模式,该模式可以使用BroadcastChannel API,因此事件不再只是冒泡,它们甚至可以跨浏览器上下文发出。

  • Listen是一个为您连接的装饰器addEventListener,因为谁愿意一直输入这个?

Readymade 现已升级至 v1

所以去GitHub上看看吧。文档门户是用 Readymade 构建的,可以在Github Pages上找到。

文章来源:https://dev.to/steveblue/why-i-coded-a-micro-library-for-web-components-46ig