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

@libeilong/model-manager

v0.3.0

Published

一个高性能、内存安全且类型友好的 TypeScript 领域模型管理库。专为复杂的前端应用设计,支持 **自动内存管理 (WeakRef)**、**响应式框架集成 (Vue/MobX)**、**关系自动 hydrate**、**模型家族多态分发** 以及 **精细化的数据合并策略**。

Downloads

274

Readme

Model Manager

一个高性能、内存安全且类型友好的 TypeScript 领域模型管理库。专为复杂的前端应用设计,支持 自动内存管理 (WeakRef)响应式框架集成 (Vue/MobX)关系自动 hydrate模型家族多态分发 以及 精细化的数据合并策略

✨ 特性

  • 🚀 内存安全:基于 WeakRefFinalizationRegistry 实现自动垃圾回收,防止内存泄漏。
  • ⚡️ 响应式友好:支持 原地修改 (In-Place Mutation),完美适配 Vue (Reactive/Ref) 和 MobX 等响应式系统,保持引用稳定性。
  • 🔄 智能合并:提供 replacemergeappend 多种策略处理嵌套关系和数组更新。
  • 🔗 关系管理:轻松定义一对一、一对多关系,自动处理循环引用和延迟加载。
  • 🧠 多态分发:支持通过 @SubModelOf 按原始数据自动落到正确业务子类。
  • 🧱 上下文隔离:支持通过 familyRoot 建立业务线级缓存隔离与关系传播边界。
  • 💎 类型增强:强大的 TypeScript 类型推导,自动生成 create 方法的输入类型定义。
  • 📦 序列化控制:支持多种序列化策略(仅主键、完整对象、混合模式),灵活应对 API 提交需求。

📦 安装

npm install @libeilong/model-manager
# 或者
yarn add @libeilong/model-manager

🚀 快速开始

如果你的场景是 nexuos 这类树节点 / 业务子类分发,建议直接跳到后面的“多态子类分发”章节。

1. 定义模型

你可以传入自定义的基类(如 Vue 的响应式基类),库会自动混入(Mixin)模型管理能力。

import { createBaseModel, Model, PrimaryKey, Relation, PK_PROP } from '@libeilong/model-manager'

// 模拟一个响应式基类 (适配 Vue/MobX 等)
class ReactiveBase {}

// 1. 创建基础模型,混入能力
@Model({ cache: false, serialization: 'hybrid' })
class BaseModel<T = 'id'> extends createBaseModel(ReactiveBase) {
  // 声明 PK_PROP 用于类型推导窄化
  declare [PK_PROP]: T

  @PrimaryKey()
  id: number = 0

  createdAt: Date = new Date()
}

// 2. 定义 User 模型
@Model({ cache: true, mergeOnCacheHit: true, serialization: 'pk' })
class User extends BaseModel {
  name: string = ''
  email?: string

  // 定义一对多关系,第二个参数 true 表示数组
  @Relation(() => Order, true)
  orders: Order[] = []

  // 普通数组(非领域模型)
  posts: Array<{ id: number; title: string }> = []
}

// 3. 定义 Order 模型
@Model({ cache: true, serialization: 'object' })
class Order extends BaseModel<'on'> {
  // 自定义主键字段名
  @PrimaryKey()
  on: string = ''

  title: string = ''
  price?: number

  // 定义一对一关系
  @Relation(() => User)
  creator!: User
}

2. 使用模型

// 创建数据
const user = User.create({
  id: 1,
  name: 'Alice',
  orders: [{ on: 'ORD-001', title: 'Book', price: 100 }],
})

// user.orders[0] 已经是 Order 类的实例
console.log(user.orders[0] instanceof Order) // true

// 再次创建相同 ID 的 User (缓存命中)
const user2 = User.create({
  id: 1,
  name: 'Alice Updated', // mergeOnCacheHit: true,属性会自动更新
})

console.log(user === user2) // true,引用完全相同
console.log(user.name) // "Alice Updated",实现原地更新

📖 API 文档

装饰器

@Model(options: ModelOptions)

配置类的全局行为。

| 选项 | 类型 | 说明 | 默认值 | | :---------------- | :----------------------------- | :------------------------------------------------------- | :--------- | | cache | boolean | 是否启用实例缓存 | false | | cacheStrategy | 'weak' \| 'strong' | 缓存策略。weak 使用弱引用(推荐),strong 为强引用。 | 'weak' | | serialization | 'hybrid' \| 'pk' \| 'object' | 默认序列化策略。 | 'hybrid' | | mergeOnCacheHit | boolean | 当 create 命中缓存时,是否自动合并新数据。 | false | | primaryKey | string | 主键字段名(通常建议使用 @PrimaryKey 装饰器)。 | - | | familyRoot | Class \| () => Class | 高级模式下指定业务线根类,用于上下文级缓存隔离。 | - |

配置继承规则:

  • 普通配置字段使用“子类覆盖父类”的规则。
  • hooks.beforeCreate 会沿继承链按“父类 -> 子类”顺序组合执行。
  • hooks.afterCreate 会沿继承链按“父类 -> 子类”顺序组合执行。
  • 实例上的 afterCreate() 会在所有配置级 afterCreate 执行完成后再执行。

@PrimaryKey()

标记类的属性为主键。库依赖主键进行缓存去重。

@Relation(targetGetter, isArray?, config?)

定义模型间的关联关系。

  • targetGetter: () => Class。使用函数返回类,解决循环引用问题。
  • isArray: boolean。是否为数组关系。
  • config: 序列化配置等。

@SubModelOf(baseCtor, match)

把当前类注册为某个模型家族下的业务子类。

  • baseCtor: 作为分发入口的基类或业务线根类。
  • match: 匹配函数,命中后会自动实例化为当前子类。

多态子类分发

默认模式:一个家族内自动落到正确子类

import {
  createBaseModel,
  Model,
  PK_PROP,
  PrimaryKey,
  Relation,
  SubModelOf,
} from '@libeilong/model-manager'

@Model({ cache: true, mergeOnCacheHit: true })
class FsNode extends createBaseModel() {
  declare [PK_PROP]: 'id'

  @PrimaryKey()
  id: string = ''

  mimetype = ''
  name = ''

  @Relation(() => FsNode, true)
  children: FsNode[] = []
}

@SubModelOf(FsNode, (raw) => raw?.mimetype === 'scene')
class SceneFsNode extends FsNode {
  get assets() {
    return this.children.filter((it) => it.mimetype === 'scene-asset')
  }
}

const node = FsNode.create({
  id: 'scene-1',
  mimetype: 'scene',
  children: [{ id: 'asset-1', mimetype: 'scene-asset' }],
})

console.log(node instanceof SceneFsNode) // true
console.log(node.assets.length) // 1

这个模式下:

  1. 关系字段仍然可以声明为基类
  2. hydrate 时会根据原始数据自动落到正确业务子类
  3. 同一家族下会共享缓存,避免出现同 id 的多个实例

高级模式:按业务线隔离上下文

当同一个基础模型在多个业务域里需要不同语义,或者希望关系树严格留在某个业务上下文内时,可以开启 familyRoot

@Model({ cache: true, mergeOnCacheHit: true })
class FsNode extends BaseModel {
  @Relation(() => FsNode, true)
  children: FsNode[] = []
}

@Model({ familyRoot: () => VideoLineFsNode })
class VideoLineFsNode extends FsNode {
  @Relation(() => VideoLineFsNode, true)
  children: VideoLineFsNode[] = []
}

@SubModelOf(VideoLineFsNode, (raw) => raw?.mimetype === 'scene')
class SceneFsNode extends VideoLineFsNode {}

这个模式下:

  1. 不同 familyRoot 会拥有各自的缓存空间
  2. 相同 id 在不同业务线上不会误命中同一实例
  3. 关系 hydrate 会沿业务线根类继续传播,而不是回落到通用基类语义

设计边界

  1. @SubModelOf 解决的是“创建或 hydrate 时自动落到哪个子类”
  2. familyRoot 解决的是“缓存和关系传播是否需要按业务线上下文隔离”
  3. 库不会自动为 children 反向补 parent,如果原始数据里没有该字段,就不会凭空生成

核心方法 (Mixin)

继承 createBaseModel 后,你的类将获得以下静态方法和实例方法。

静态方法

  • static create(data, options?) 创建或获取模型实例。如果缓存中存在且开启了缓存,则返回已有实例。

    • data: 强类型的输入对象(支持递归嵌套)。
    • options.merge: 强制覆盖类配置的合并行为。
  • static createReference(pk) 仅通过主键创建一个“引用状态”的实例(标记为 isLoaded: false),常用于延迟加载场景。

  • static find(pk) 从缓存中查找实例。

  • static findAll() 获取当前模型家族下已缓存的所有实例。

  • static clearCache() 清空当前模型家族下的缓存。常用于测试或显式重置场景。

实例方法

  • merge(data, options?) 增量合并数据到当前实例。这是实现响应式更新的核心。

    user.merge(
      { name: 'Bob' },
      {
        // 数组合并策略:
        // 'replace' (默认): 以新数组为准,同步更新内容
        // 'merge': 保留本地多余项,合并新项
        // 'append': 追加新项
        arrayStrategy: 'replace',
      },
    )
  • hooks 执行顺序

    @Model({
      hooks: {
        beforeCreate: (data) => ({ ...data, stage: ['parent-before'] }),
      },
    })
    class ParentModel extends BaseModel {}
    
    @Model({
      hooks: {
        beforeCreate: (data) => ({
          ...data,
          stage: [...(data.stage || []), 'child-before'],
        }),
        afterCreate: (instance) => {
          instance.stage.push('child-after')
        },
      },
    })
    class ChildModel extends ParentModel {
      stage: string[] = []
    
      afterCreate() {
        this.stage.push('instance-after')
      }
    }
    
    const model = ChildModel.create({ id: 1 })
    // beforeCreate: parent -> child
    // afterCreate: parent-config -> child-config -> instance-afterCreate
  • serialize(options?) 将模型转换为普通 JSON 对象。

    • 支持处理循环引用(自动降级为主键)。
    • 根据 @Model 配置决定关系字段是输出 ID 还是完整对象。
  • load(opts?) 触发数据加载。需要用户在类上实现静态 _fetchData 方法。

    // 在类定义中实现
    static async _fetchData(pk, context) {
      return await api.getUser(pk);
    }
    
    // 调用
    await user.load();
    await user.load({ force: true });

💡 核心概念与最佳实践

1. 响应式集成 (Vue/MobX)

本库的设计初衷之一是配合现代前端框架。

  • 原地修改 (In-Place Mutation)merge 方法会直接修改对象属性,而不是替换对象引用。
  • 基类继承:通过 createBaseModel(Base),你可以传入 Vue 的类或其他基类,使得所有生成的 Model 实例天然具备响应式能力。

2. 内存管理机制

为了防止随着应用运行 Model 实例无限堆积,默认使用 WeakRef + FinalizationRegistry

  • 当 UI 组件卸载,不再持有 Model 引用时,GC 会自动回收 Model。
  • 库会自动监听到回收事件,并从内部缓存 Map 中清理对应的 Key,防止内存泄漏。

3. 数据合并策略 (replace vs merge)

在处理数组关系时(例如更新 user.orders):

  • replace (默认):视为“同步”。后端返回的列表就是最新状态。本地多余的项会被移除,存在的项会原地更新属性,新的项会追加。
  • merge:视为“合并”。保留本地所有项,并把后端返回的数据更新进去。适用于“加载更多”或“增量推送”场景。