# @amadeus-it-group/tansu 中文文档

规范地址：https://npmcn.com/package/%40amadeus-it-group/tansu/

版本：2\.0\.0
npm 元数据更新时间：2025-02-18T09:49:55.084Z
中文译文更新时间：2026-11-05T23:23:00.785Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40amadeus-it-group/tansu
原文对照：https://npmcn.com/package/%40amadeus-it-group/tansu/?lang=original
项目主页：https://github.com/AmadeusITGroup/tansu#readme
源码仓库：https://github.com/AmadeusITGroup/tansu

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：129 kB，23 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2022-06-20T14:19:17.416Z
- 最新版本发布：2024-12-04T15:46:18.891Z
- 近 30 天下载量：15147（2026-10-06 至 2026-11-04，npm 官方下载统计）
- 已发布版本数：25（近 12 个月 0 个）

## 中文简介

tansu是一个轻量级,以推为主的框架-不可知的状态管理库. 它借用了最初由斯维尔特商店设计和实施的构思和API,并通过计算和批量扩展.

## 安装

```sh
npm install @amadeus-it-group/tansu
```

也可以使用：`pnpm add @amadeus-it-group/tansu`、`yarn add @amadeus-it-group/tansu`、`bun add @amadeus-it-group/tansu`

## README

# 田苏

[![npm](https://img.shields.io/npm/v/@amadeus-it-group/tansu)](https://www.npmjs.com/package/@amadeus-it-group/tansu) ![build](https://github.com/AmadeusITGroup/tansu/workflows/ci/badge.svg) [![codecov](https://codecov.io/gh/AmadeusITGroup/tansu/branch/master/graph/badge.svg)](https://codecov.io/gh/AmadeusITGroup/tansu)

田生是一个轻量级,以推为主的框架-不可知析的州立管理图书馆. 它借用了最初设计和实施的理念和API [斯维尔特商店](https://github.com/sveltejs/rfcs/blob/master/text/0002-reactive-stores.md) 并延长他们的寿命 `computed` 财务报告和审定财务报表 `batch`。 。 。 .

主要特点:

-   具有可表达性和非常灵活的API(功能和类)的小概念表面;
-   可用于创建“本地”(模块级或组件级),协作商店;
-   能够处理不可移动和可变的数据;
-   产生绝对最低锅炉板的紧凑码.

执行明智,这是一个很小的(1300 LOC)图书馆,没有任何外部依赖.

## 安装

您可以安装此选项, 将 Tansu 添加到您的工程中 `@amadeus-it-group/tansu` 使用您最喜欢的软件包管理器的软件包, 例如:

-   `yarn add @amadeus-it-group/tansu`
-   `npm install @amadeus-it-group/tansu`

## 使用量

看看这个 [Tansu API 文档](https://amadeusitgroup.github.io/tansu/)。 。 。 .

API管理您反应状态的功能部分可分为三个不同的组别:

-   基地存储 : `writable`
-   已计算商店 : `derived`, (中文(简体) ), `computed`, (中文(简体) ), `readable`
-   公用事业: `batch`, (中文(简体) ), `asReadable`, (中文(简体) ), `asWritable`

### 可写

[api 文档](https://amadeusitgroup.github.io/tansu/functions/writable.html)

**书面:基本建筑**

A 类 `writable` 作为“储存”的基础要素——一个容器,旨在封装一个值,允许观察和修改其状态。 您可以使用 `set` 或者说 `update` 方法。 。 .

当值发生变化时收到通知 `subscribe()` 方法,与回调功能相配,可以被使用.

#### 基本用途

```typescript
import {writable} from "@amadeus-it-group/tansu";
const value$ = writable(0);

const unsubscribe = values$.subscribe((value) => {
  console.log(`value = ${value}`);
});

value$.set(1);
value$.update((value) => value + 1);
```

输出 :

```text
  value = 0
  value = 1
  value = 2
```

#### 设置和拆解

可写文件的第二个参数允许在至少一个订阅者订阅或没有更多订阅者时收到通知.

```typescript
import {writable} from "@amadeus-it-group/tansu";

const value$ = writable(0, () => {
  console.log('At least one subscriber');

  return () => {
    console.log('No more subscriber');
  }
});

const unsubscribe = values$.subscribe((value) => {
  console.log(`value = ${value}`);
});

value$.set(1);
unsubscribe();
```

输出 :

```text
  At least one subscriber
  value = 0
  value = 1
  No more subscriber
```

### 来源

[api 文档](https://amadeusitgroup.github.io/tansu/functions/derived.html)

A 类 `derived` 存储根据作为参数提供的一个或多个其他存储计算其值. 由于它的价值来自其他商店,它是一个只读的商店,没有任何 `set` 或者说 `update` 方法。 。 .

#### 单行店

```typescript
import {writable, derived} from "@amadeus-it-group/tansu";

const value$ = writable(1);
const double$ = derived(value$, (value) => value * 2);

double$.subscribe((double) => console.log('Double value', double));
value$.set(2);
```

输出 :

```text
Double value 2
Double value 4
```

#### 多个仓库

```typescript
import {writable, derived} from "@amadeus-it-group/tansu";

const a$ = writable(1);
const b$ = writable(1);
const sum$ = derived([a$, b$], ([a, b]) => a + b);

sum$.subscribe((sum) => console.log('Sum', sum));
a$.set(2);
```

输出 :

```text
Sum 2
Sum 3
```

#### 同步集

A 类 `derived` 可以直接使用设定方法来操纵其值,而不是依赖所提供函数的返回值. 这种灵活性允许您在更新可观测值之前管理同步操作或应用过滤逻辑.

```typescript
import {writable, derived} from "@amadeus-it-group/tansu";

const a$ = writable(0);
const asynchronousDouble$ = derived(a$, (a, set) => {
  const plannedLater = setTimeout(() => set(a * 2));
  return () => {
    // This clean-up function is called if there is no listener anymore,
    // or if the value of a$ changed
    // In this case, the function passed to setTimeout should not be called
    // (if it was not called already)
    clearTimeout(plannedLater);
  };
}, -1);

const evenOnly$ = derived(a$, (a, set) => {
  if (a % 2 === 0) {
      set(a);
  }
}, <number | undefined>undefined);

asynchronousDouble$.subscribe((double) => console.log('Double (asynchronous)', double));
evenOnly$.subscribe((value) => console.log('Even', value));

a$.set(1);
a$.set(2);
```

输出 :

```text
Double (asynchronous) -1
Even 0
Even 2
Double (asynchronous) 4
```

### 计算

[api 文档](https://amadeusitgroup.github.io/tansu/functions/computed.html)

A 类 `computed` 储存是衍生商店的另一变种,具有以下特性:

-   **隐性依赖:** 与衍生商店不同,不要求明确声明依赖关系.
    
-   **动态依赖听觉 :** 依附依据其用法确定. 这意味着不积极使用的依赖性不会自动被"征集"到,优化资源利用.
    

#### 切换映射

这种订阅/不订阅依赖的能力可以自然地创建切换地图.

```typescript
import {writable, computed} from "@amadeus-it-group/tansu";

const switchToA$ = writable(true);
const a$ = writable(1);
const b$ = writable(0);

const computedValue$ = computed(() => {
  if (switchToA$()) {
    console.log('Return a$');
    return a$();
  } else {
    console.log('Return b$');
    return b$();
  }
});

computedValue$.subscribe((value) => console.log('Computed value:', value));
a$.set(2);
switchToA$.set(false);
a$.set(3);
a$.set(4);
switchToA$.set(true);

```

输出 :

```text
Return a$
Computed value: 1
Return a$
Computed value: 2
Return b$
Computed value: 0
Return a$
Computed value: 4
```

何时 `switchToA$.set(false)` 订阅 `a$` 已取消,这意味着随后的更改将改为 `a$` 将不再触发计算。只有在切换ToA$回到真实状态时才再次进行计算.

### 可读

[api 文档](https://amadeusitgroup.github.io/tansu/functions/readable.html)

与斯维尔特商店类似,此函数生成一个无法外部修改值的商店.

```typescript
import {readable} from '@amadeus-it-group/tansu';

const time = readable(new Date(), (set) => {
  const interval = setInterval(() => {
    set(new Date());
  }, 1000);

  return () => clearInterval(interval);
});
```

### 生成 vs 计算

虽然推导和计算可能看起来相似,但它们显示出不同的特征,可以根据使用案例对有效性产生重大影响:

-   **属地申报:**
    
    -   `computed`:不需要明确宣布依赖性,为代码构成提供了更大的灵活性.
    -   `derived`:要求明确宣布依赖关系.
-   **性能 :**
    
    -   `computed`: 仅根据上次运行中涉及的商店的变化,通过重新运行该函数来提高性能.
    -   `derived`:每次依赖的存储更改时重运行该函数.
-   **同步状态 :**
    
    -   `computed`: 无法管理同步状态.
    -   `derived`: 能够处理与 `set` 方法.
-   **跳过值排放 :**
    
    -   `computed`: 不提供跳过排放值的机制.
    -   `derived`: 允许通过选择不调用所提供值来跳过值的释放 `set` 方法.
-   **设置和拆分 :**
    
    -   `computed`:缺乏明确的设置和拆分方法.
    -   `derived`: 支持设置和拆卸方法,允许添加或去除DOM收听器等动作.
    -   当最后的收听器取消订阅,然后再次订阅时,由于它的设置-teardown功能,所衍生的函数会被重新运行. 相比之下,一个计算提供了最后一个值而不重算 如果依赖性在此期间没有改变.

虽然 `computed` 感觉更直观 在许多用例, `derived` 在下列情况中表现突出: `computed` 特别是管理同步状态和对价值排放进行更严格的控制.

根据具体要求仔细选择它们可以提高应用程序中国家管理的有效性.

### 获得值

获得商店价值有三种方法:

```typescript
import {writable, get} from "@amadeus-it-group/tansu";

const count$ = writable(1);
const unsubscribe = count$.subscribe((count) => {
  // Will be called with the updated value synchronously first, then each time count$ changes.
  // `unsubscribe` must be called to prevent future calls.
  console.log(count);
});

// A store is also a function that you can call to get the instant value.
console.log(count$());

// Equivalent to
console.log(get(count$));
```

> \[注\] 获取即时值意味着商店上的订阅和未下架:
> 
> -   在设置/设置函数时,了解可能很重要.
> -   在同一范围, 宁愿将值存储一次到本地变量中, 而不是调用 `store$()` 数次.
> 
> 当在反应语境中(即在计算语境中)被调用时,获得值服务以了解并"倾听"附属商店.

### 批次

[api 文档](https://amadeusitgroup.github.io/tansu/functions/batch.html)

与其他像有信号的Angular或有runes的Svelte这样的图书馆相反,我们维持了商店更改和他们在Tansu的订阅同步的制约,因为订阅的回调是同步执行的(通常被引用为"效果").

虽然这些框架可以接受推迟这些呼吁,因为其目标事先众所周知(以优化其最终表述),但田秀的情况并非如此,因为其目标要适应任何情况.

同步订阅的问题在于它可以创建"滑翔". 用户和计算出的存储回调次数太多,可产生不正确的中间值.

Svelte 商店解决了 [钻石依赖问题](https://github.com/sveltejs/svelte/pull/2955),但它并不匹配所有使用大小写.

举一例曰:

```typescript
import {writable, derived} from '@amadeus-it-group/tansu';

const firstName = writable('Arsène');
const lastName = writable('Lupin');
const fullName = derived([firstName, lastName], ([a, b]) => `${a} ${b}`);
fullName.subscribe((name) => console.log(name)); // logs any change to fullName
firstName.set('Sherlock');
lastName.set('Holmes');
console.log('Process end');
```

输出 :

```text
Arsène Lupin
Sherlock Lupin
Sherlock Holmes
Process end
```

全数 名称商店相继经历了不同的状态,包括一个不一致的状态,因为 `Sherlock Lupin` 不存在! 即使它可以被视为仅仅是一个中间状态,它是 **基础** 州管理层只管理一致的数据,以防止出现问题并优化代码.

在坦苏 `batch` 可供推迟 **同步** 订户打电话,事实上是受抚养人 `derived` 或者说 `computed` 计算以解决各种多重依赖性问题.

前面的例子是这样解决的:

```typescript
import {writable, derived, computed, batch} from '@amadeus-it-group/tansu';

const firstName = writable('Arsène');
const lastName = writable('Lupin');
const fullName = derived([firstName, lastName], ([a, b]) => `${a} ${b}`);
// note that the fullName store could alternatively be create with computed:
// const fullName = computed(() => `${firstName()} ${lastName()}`);
fullName.subscribe((name) => console.log(name)); // logs any change to fullName
batch(() => {
    firstName.set('Sherlock');
    lastName.set('Holmes');
});
console.log('Process end');
```

输出 :

```text
Arsène Lupin
Sherlock Holmes
Process end
```

> \[注\]
> 
> -   获取商店的直接值( 使用前面提到的三种方法中的任何一种: 调用) `subscribe` 与将同步调用值的订阅器,使用 `get` 或称商店为函数)总是根据所有附属商店的最新价值提供值(即使这需要重算批次内计算出或衍生出的计算值)
> -   所有给订阅者的呼叫(不包括订阅过程中的第一次同步呼叫)被推迟到批次结束
> -   如果一个订阅者已经得到该批内新值的通知(例如,当它是该批内登记的新订阅者时),如果该值保持不变,则在该批后期将不会再次得到通知. 订阅者只有在店面价值自其最后一次调用后有所变化时才会被引用.
> -   `batch` 可以在内部调用 `batch`。订阅者的呼叫在第一批的末尾同步进行.

### 可读

[api 文档](https://amadeusitgroup.github.io/tansu/functions/asReadable.html)

`asReadable` 返回只披露订阅该商店所需的基本要素的新商店。 它还包括作为参数通过的任何其他方法.

这是有用的,并被广泛用来组成一个自定义商店:

-   第一个参数是可写存储,
-   第二个参数是可读存储返回的扩展对象.

```typescript
import {writable, asReadable} from "@amadeus-it-group/tansu";

function createCounter(initialValue: number) {
  const store$ = writable(initialValue);

  return asReadable(store$, {
    increment: () => store$.update((value) => value + 1),
    decrement: () => store$.update((value) => value - 1),
    reset: () => store$.set(initialValue)
  });
}

const counter$ = createCounter(0);

counter$.subscribe((value) => console.log('Value: ', value));

counter$.increment();
counter$.reset();
counter$.set(2); // Error, set does not exist
```

输出 :

```text
Value: 0
Value: 1
Value: 0
(Error thrown !)
```

### 可写入

[api 文档](https://amadeusitgroup.github.io/tansu/functions/asWritable.html)

`asWritable` 几乎与一个 `asReadable`,关键区别在于执行 [可写符号](https://amadeusitgroup.github.io/tansu/interfaces/WritableSignal.html) 接口.

当您想要将您的计算存储连接到原存储时, 或者执行自定义时, 会有帮助 `set` 方法。 这个 `set` 方法可以在第二个参数中或对象内直接传递,类似于在 `asReadable`。 。 。 .

例如:

```typescript
import {writable, asWritable} from "@amadeus-it-group/tansu";

const number$ = writable(1);
const double$ = computed(() => number$() * 2);
const writableDouble$ = asWritable(double$, (doubleNumber) => {
  number$.set(doubleNumber / 2);
});
/* equivalent to:
  const writableDouble$ = asWritable(double$, {
    set: (doubleNumber) => number$.set(doubleNumber / 2)
  });
*/

writableDouble$.subscribe((value) => console.log('Value: ', value));

writableDouble$.set(2); // Nothing is triggered here, as number$ is already set with 1
writableDouble$.set(4);

```

输出 :

```text
Value: 2
Value: 4
```

## 纳入框架

### 田生与斯维尔特框架合作得很好

田生被设计为并保持与斯维尔特完全相容.

### 田生与角生态系统合作得很好

以下是一个使用 Tansu 商店的角构件的例子:

```typescript
import { Component } from "@angular/core";
import { AsyncPipe } from '@angular/common';
import { Store, computed, get } from "@amadeus-it-group/tansu";

// A store is a class extending Store from Tansu
class CounterStore extends Store<number> {
  constructor() {
    super(0); // initialize store's value (state)
  }

  // implement state manipulation logic as regular methods
  increment() {
    // create new state based on the current state
    this.update(value => value + 1);
  }

  reset() {
    // replace the entire state with a new value
    this.set(0);
  }
}

@Component({
  selector: "my-app",
  template: `
    <button (click)="counter$.increment()">+</button> <br />

    <!-- store values can be displayed in a template with the standard async pipe -->
    Counter: {{ counter$ | async }} <br />
    Double counter: {{ doubleCounter$ | async }} <br />
  `,
  standalone: true,
  imports: [AsyncPipe]
})
export class App {
  //  A store can be instantiated directly or registered in the DI container
  counter$ = new CounterStore();

  // One can easily create computed values by specifying a transformation function
  doubleCounter$ = computed(() => 2 * get(this.counter$));
}
```

这个例子虽然相当少,但表明大部分的Tansu API带有Angular.

-   工作与标准 `async` 烟斗出盒子
-   在任何级别(模块或组件注射器)的依赖性注射容器中均可注册储存
-   存储可以很容易地与 rxjs 一起使用,因为它们执行 `InteropObservable` 接口
-   反之,rxjs可观测到(或任何执行 `InteropObservable` 界面)与 Tansu(例如 Tansu) 很容易使用 `computed` 或者说 `derived`) (中文(简体) ).

## 对项目的贡献

请检查一下 [D. 发 展](https://npmcn.com/package/%40amadeus-it-group/tansu/DEVELOPER.md) 在本地开发机器上建立和测试项目的文件.

## 信贷和前款

-   [丝绒](https://github.com/sveltejs/rfcs/blob/master/text/0002-reactive-stores.md) 获得初始想法和API设计的所有信用.
-   [NgRx 组件仓库](https://hackmd.io/zLKrFIadTMS2T6zCYGyHew?view) 以不同的方法解决类似的问题.

## 常见问题

### @amadeus-it-group/tansu 是什么？

tansu是一个轻量级,以推为主的框架-不可知的状态管理库\. 它借用了最初由斯维尔特商店设计和实施的构思和API,并通过计算和批量扩展\.

### 如何安装 @amadeus-it-group/tansu？

在项目目录运行 npm install @amadeus-it-group/tansu，或使用 pnpm add / yarn add / bun add。

### @amadeus-it-group/tansu 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @amadeus-it-group/tansu 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @amadeus-it-group/tansu 可以商用吗？

@amadeus-it-group/tansu 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @amadeus-it-group/tansu 的下载量有多少？

根据 npm 官方下载统计，@amadeus-it-group/tansu 在 2026/10/06 – 2026/11/04 这 30 天内被下载 15,147 次，日均约 505 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @amadeus-it-group/tansu 还在维护吗？

最新版本 2\.0\.0 发布于 2024/12/04（1\.9 年前）；至今共发布 25 个版本，其中过去 12 个月发布了 0 个；首次发布于 2022/06/20。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

