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

2025-06-07

我为什么要为 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
PREV
使用 Solidity 在以太坊区块链上编写 ERC20 代币预售智能合约的综合指南
NEXT
Web Components 与 React 这不是 Web Components 与 React 的问题