npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

subay

v0.0.12

Published

A lightweight reactive UI library with state management and template rendering

Downloads

439

Readme

Subay

Subay 是一个轻量级的响应式编程库,提供了状态管理和 DOM 操作的功能。

API

root

简介 创建一个根更新上下文,用于管理响应式状态的生命周期。

语法

root<T>(fn: (destroy: () => void) => T): T

参数

  • fn: 一个函数,接收一个 destroy 函数作为参数,用于手动销毁根上下文。

返回值

  • T: fn 函数的返回值。

例子

import { root, o, S } from 'subay';

root((destroy) => {
    const count = o(0);
    const doubled = S(() => count() * 2);

    console.log(doubled()); // 0
    count(1);
    console.log(doubled()); // 2

    // 手动销毁
    destroy();
});

注意

销毁使用 root 创建的上下文的唯一方式就是调用 destroy 回调。

S

简介 创建一个计算值,当依赖的可观察值变化时自动重新计算。

语法

S<T>(fn: (pv: T | undefined) => T, value?: T): IS<T>

参数

  • fn: 计算函数,接收上一次的计算值作为参数。
  • value: 可选的初始值。

返回值

  • IS<T>: 一个函数,调用时返回计算值。

例子

import { S, o } from 'subay';

const count = o(0);
const doubled = S(() => count() * 2);

console.log(doubled()); // 0
count(1);
console.log(doubled()); // 2

o / observable

简介 创建一个可观察值,用于存储和更新状态。

语法

o<T>(value: T): IO<T>

参数

  • value: 初始值。

返回值

  • IO<T>: 一个函数,无参数调用时返回当前值,带参数调用时更新值并返回新值。

例子

import { o } from 'subay';

const count = o(0);
console.log(count()); // 0
count(1);
console.log(count()); // 1

cleanup

简介 注册一个清理函数,当当前更新上下文被销毁时执行。

语法

cleanup<T extends () => void>(f: T): T

参数

  • f: 清理函数。

返回值

  • T: 传入的清理函数。

例子

import { root, S, o, cleanup } from 'subay';

root(() => {
    const count = o(0);

    S(() => {
        console.log(count());
        cleanup(() => {
            console.log('Cleanup called');
        });
    });

    count(1);
});
// 输出: 0, 1, Cleanup called

transaction

简介 创建一个事务,批量更新可观察值,避免中间状态的计算。

语法

transaction<T>(f: () => T): T

参数

  • f: 事务函数,在其中可以更新多个可观察值。

返回值

  • T: f 函数的返回值。

例子

import { transaction, o, S } from 'subay';

const a = o(1);
const b = o(2);
const sum = S(() => a() + b());

S(() => console.log(sum())); // 3

transaction(() => {
    a(2);
    b(3);
});
// 输出: 5 (只计算一次)

sample

简介 在不建立依赖关系的情况下获取可观察值的值。

语法

sample<T>(f: () => T): T

参数

  • f: 函数,在其中可以访问可观察值但不会建立依赖关系。

返回值

  • T: f 函数的返回值。

例子

import { sample, o, S } from 'subay';

const count = o(0);
let cachedValue;

S(() => {
    // 不建立依赖关系
    cachedValue = sample(() => count());
    console.log(cachedValue);
});

count(1); // 不会触发重新计算
console.log(cachedValue); // 0

isListening

简介 检查当前是否处于响应式监听状态。

语法

isListening(): boolean

参数

返回值

  • boolean: 当前是否处于响应式监听状态。

例子

import { isListening, S } from 'subay';

console.log(isListening()); // false

S(() => {
    console.log(isListening()); // true
});

subscribe

简介 订阅一个函数,当依赖的可观察值变化时自动执行。

语法

subscribe(f: () => void): () => void

参数

  • f: 订阅函数。

返回值

  • () => void: 取消订阅的函数。

例子

import { subscribe, o } from 'subay';

const count = o(0);
const unsubscribe = subscribe(() => {
    console.log(count());
});

count(1); // 输出: 1
count(2); // 输出: 2

unsubscribe();
count(3); // 无输出

unsubscribe

简介 取消订阅一个函数。

语法

unsubscribe(f: () => void): void

参数

  • f: 要取消订阅的函数。

返回值

例子

import { subscribe, unsubscribe, o } from 'subay';

const count = o(0);
const fn = () => console.log(count());

subscribe(fn);
count(1); // 输出: 1

unsubscribe(fn);
count(2); // 无输出

isObservable

简介 检查一个值是否是可观察值。

语法

isObservable(o: any): o is IO<any>

参数

  • o: 要检查的值。

返回值

  • boolean: 是否是可观察值。

例子

import { isObservable, o, S } from 'subay';

const count = o(0);
const doubled = S(() => count() * 2);

console.log(isObservable(count)); // true
console.log(isObservable(doubled)); // false

isComputed

简介 检查一个值是否是计算值。

语法

isComputed(o: any): o is IS<any>

参数

  • o: 要检查的值。

返回值

  • boolean: 是否是计算值。

例子

import { isComputed, o, S } from 'subay';

const count = o(0);
const doubled = S(() => count() * 2);

console.log(isComputed(count)); // false
console.log(isComputed(doubled)); // true

isReactive

简介 检查一个值是否是响应式值(可观察值或计算值)。

语法

isReactive(o: any): o is IO<any> | IS<any>

参数

  • o: 要检查的值。

返回值

  • boolean: 是否是响应式值。

例子

import { isReactive, o, S } from 'subay';

const count = o(0);
const doubled = S(() => count() * 2);

console.log(isReactive(count)); // true
console.log(isReactive(doubled)); // true
console.log(isReactive(42)); // false

map

简介 将一个数组映射为 DOM 节点数组,当数组变化时自动更新。

语法

map<T>(array: () => T[], f: (item: T) => Node[]): Node[]

参数

  • array: 一个返回数组的函数。
  • f: 映射函数,将数组项转换为 DOM 节点数组。

返回值

  • Node[]: DOM 节点数组。

例子

import { map, o, html } from 'subay';

const items = o(['a', 'b', 'c']);
const list = map(
    () => items(),
    (item) => html`<li>${item}</li>`,
);

document.body.append(...list);
// 渲染: <li>a</li><li>b</li><li>c</li>

items(['a', 'b', 'c', 'd']);
// 自动更新: <li>a</li><li>b</li><li>c</li><li>d</li>

svg

简介 创建 SVG 元素的模板标签函数。

语法

svg(segments: TemplateStringsArray, ...values: any[]): Node[]

参数

  • segments: 模板字符串的静态部分。
  • values: 模板字符串的动态部分。

返回值

  • Node[]: SVG 元素节点数组。

例子

import { svg, o } from 'subay';

const radius = o(50);
const circle = svg`<circle cx="100" cy="100" r="${radius}" fill="red"/>`;

document.getElementById('svg').append(...circle);

html

简介 创建 HTML 元素的模板标签函数。

语法

html(segments: TemplateStringsArray, ...values: any[]): Node[]

参数

  • segments: 模板字符串的静态部分。
  • values: 模板字符串的动态部分。

返回值

  • Node[]: HTML 元素节点数组。

例子

import { html, o } from 'subay';

const count = o(0);
const counter = html`
    <div>
        <p>Count: ${count}</p>
        <button onclick=${() => count(count() + 1)}>Increment</button>
    </div>
`;

document.body.append(...counter);
// 点击按钮会自动更新 count 的值

使用 HTML

Subay 使用 DOMParser 解析 HTML 模板, html 的参数必须是合法的 HTML 字符串,(同理, svg 的参数必须是合法的 SVG 字符串), 这是有别于 JSX 的地方。

例子

import { html } from 'subay';

// 合法的 HTML 字符串
const validHtml = html`<div><p>Hello</p></div>`;

document.body.append(...validHtml);

错误示例

import { html } from 'subay';

// 自闭合标签
const invalidHtml = html`<div>
    <span />
    <span />
</div>`; // 会导致解析成 <div><span><span></span></span></div>

传入对象作为元素属性

如果需要传入一个对象作为元素属性,可以使用 ... 语法。

语法

html`<tag ...${props}></tag>`;

例子

import { html, o } from 'subay';

const props = o({
    className: 'container',
    style: 'color: red;',
});

const element = html`<div ...${props}></div>`;

document.body.append(...element);

组件的声明

组件是一个返回 Node[] 的函数,可以接收参数。和 React、Vue 等不同,组件的参数是一个列表。

语法

const Component = (text, children: () => Node[]) => {
    return html`<div>${text}</div>`;
};

例子

import { html } from 'subay';

const Greeting = (name: string, children: () => Node[]) => {
    return html`
        <div>
            <h1>Hello, ${name}!</h1>
            ${children()}
        </div>
    `;
};

组件的使用

在模板中使用组件时,在标签处引用组件。 因为模板是 HTML 字符串,引用的时候不支持自闭合标签。 传给组件的参数必须和组件声明的参数顺序一致。 组件获得的参数总是和传入的保持一致。

例子

import { html, o } from 'subay';

const Greeting = (greet: string, name: () => string) => {
    return html`<h1>${greet}, ${name}!</h1>`;
};

const name = o('World');
const app = html` <div><${Greeting} greet=${'Hello'} name=${name}></${Greeting}></div> `;

document.body.append(...app);

错误示例

import { html, o } from 'subay';

const Greeting = (greet: string, name: () => string) => {
    return html`<h1>${greet}, ${name}!</h1>`;
};

const name = o('World');
// 错误, 后面的 span 会被当做 Greeting 的子节点。
const app = html`
    <div>
        <${Greeting}
            greet=${'Hello'}
            name=${name}
        />
        <span></span>
    </div>
`;
// 错误, 参数顺序和 Greeting 的参数不一致
const app = html` <div><${Greeting} name=${name} greet=${'Hi'} ></${Greeting}></div> `;

document.body.append(...app);

动态的组件

标签位置的插值可以是 So 的返回值,可以根据条件动态选择要渲染的组件。

例子

import { html, o, S } from 'subay';

const Greeting = (name: () => string) => {
    return html`<h1>Hello, ${name}!</h1>`;
};

const Farewell = (name: () => string) => {
    return html`<h1>Goodbye, ${name}!</h1>`;
};

const showGreeting = o(true);
const name = o('World');
const Component = S(() => (showGreeting() ? Greeting : Farewell));

const app = html`
    <div>
        <${Component} name=${name} ></${Component}>
        <button onclick=${() => showGreeting(!showGreeting())}>Toggle</button>
    </div>
`;

document.body.append(...app);