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 🙏

© 2025 – Pkg Stats / Ryan Hefner

gentx

v1.3.0

Published

> RxJS 辅助工具

Readme

GentX: 辅助RxJS的数据流管理工具。

使用RxJS更好地管理数据流。

使用此工具时请确保你已经会使用RxJS

安装使用

安装:

npm i rxjs gentx -S

使用:

import {
  logGuard,
  makeObservable,
  groupFlows,
  flowSource,
  flowSources
} from 'gentx';

概念

这里的概念指gentx新加入的概念,其他rxjs的概念请看RxJS文档, 或RxJS文档2, 或RxJS中文文档

0:GentX的数据管理方式

GentX 将应用中数据管理的所有部分分为三个部分:

  • 数据存储:自定义数据Store,LocalStore,SessionStore,Cookie等…
  • 数据流动变换:将一定事件循环内的所有数据变换抽象为数据的流动,比如点击添加用户按钮的过程,会产生这样一个数据流: 点击按钮 => 参数封装器 => 接口请求器 => 结果处理器 => 数据Store => UI
  • 数据反应:数据改变是产生一些副作用,比如修改数据自动更新UI。

1:动态数据源:Source

动态数据源(Source)是指:根据参数生成一个Observable

  • 动态数据源(Source)是一个函数
  • 动态数据源(Source)接受一个参数
  • 动态数据源(Source)执行后返回一个 observable 对象
  • 可以用在rxjs的管道操作(pipe)之中。
// can cancel source
export function canCancelSource(val) {
  let timer = null;
  let promise = new Promise((resolve, reject) => {
    timer = setTimeout(() => {
      resolve(`${val}-cancel`);
    }, 1000);
  });

  // crate a observable with a promise and a cancel function
  return makeObservable(promise, () => {
    console.log('source canceled...');
    clearTimeout(timer);
  });
}

// create a observable from a source and a value
let observable = canCancelSource(1);
let subscription = observable.subscribe({
  // ...
});
// unsubscribe
subscription.unsubscribe();

2:数据流:Flow

数据流 Flow 是指: 传入一个Observable,进行各种自定义的管道处理,然后输出一个新的Observable

  • Flow是一个函数,用来对Observable数据流进行转换。
  • 传入一个(http://reactivex.io/rxjs/manual/overview.html#observable) 对象,传出一个(http://reactivex.io/rxjs/manual/overview.html#observable) 对象。
  • 数据流转换中可以用Source来进行管道处理,比如和rxjsconcatMap,mergeMap,switchMap等操作符一起用。
import { map } from 'rxjs/operators';

export function testFlow(input) {
  return input.pipe(
    map(v => `${v}-test`)
  );
}

// can use source to concat flow
export function testFlow2(input) {
  return input.pipe(
    concatMap(source2)
  );
}

3:数据流组:Flow Group

Flow Group 主要是flow很多的情况下用来对flow进行分组,方便调用和调试。

  • Flow GroupFlow进行分组。
  • Flow Group可以添加守卫,便于调试和统一变换。
import { flowSources, groupFlows } from '../../index';
import * as TestSoureces from '../sources/test';
import { testFlow } from '../flows/test';
import { logGuard } from '../logGuard';

export const srcFlows = groupFlows(flowSources(TestSoureces), {
  groupName: 'src'
});

export const guardFlows = groupFlows({
  test: testFlow
}, {
  groupName: 'guard',
  beforeGuards: [logGuard],
  afterGuards: [logGuard]
});

4:数据流守卫:Flow Guard

数据流守卫Flow一样,只是会多接受一个参数opts,和Flow Group一起配合使用。

import { map } from 'rxjs/operators';
import { log } from '../utils';

export function logGuard(input, opts={}) {
  let {flowName, groupName, guardType} = opts;

  // not use as a middleware
  if (!guardType) return input;

  let typeMsg = { before: 'in', after: 'out' }[guardType];

  return input.pipe(
    map(value => {
      let logData;

      try {
        logData = JSON.parse(JSON.stringify(value));
      } catch(e) {
        logData = e.message;
      }

      log(`[gentx log] ~ flow ${typeMsg} <${groupName}>.<${flowName}>:`, logData);

      return value;
    })
  );
}

API

makeObservable(input, cancel)

根据一个ObservableInput: input,创建一个新的Observable对象。这个新的Observable对象如被取消订阅,会调用函数cancel

groupFlows(flowMap={}, opts={}):

创建一个Flow 分组。

  • flowMap: flow集合,{flowName: flowFn}的格式。

  • opts默认为:

    {
      groupName= 'Anonymous', //分组名,便于调试
      beforeGuards= [], // 前置守卫
      afterGuards= [] // 后置守卫
    }
import { flowSources, groupFlows } from '../../index';
import * as TestSoureces from '../sources/test';
import { testFlow } from '../flows/test';
import { logGuard } from '../logGuard';

export const guardFlows = groupFlows({
  test: testFlow
}, {
  groupName: 'guard',
  beforeGuards: [logGuard],
  afterGuards: [logGuard]
});

flowSource(source, operatorType='concatMap')

根据Source创建一个Flow

operatorType可以取三个值:concatMap, mergeMap, switchMap。

flowSources(sourceMap, operatorType='concatMap')

根据一组Source返回一个Flow集合,内部调用flowSource

import { flowSources, groupFlows } from '../../index';
import * as TestSoureces from '../sources/test';
import { testFlow } from '../flows/test';
import { logGuard } from '../logGuard';

export const srcFlows = groupFlows(flowSources(TestSoureces), {
  groupName: 'src'
});

logGuard

内置守卫,打印调试信息。

推荐目录结构

----data           ## 所有数据相关存在data目录
  |---- apis       ## api请求
  |---- sources    ## 动态数据源
  |---- flows      ## 数据流
  |---- stores     ## 数据存储

推荐和其他工具配合使用

React一起使用

建议:rxjs + gentx + mobx + mobx-react + react

为react组件提供一个装饰器gentx, 使用装饰器后,组件实例会多两个属性$bindSub, $unsubscribe。 不能用于function组件,如果与其他装饰器一起使用,确保gentx是最接近组件的。

  • $bindSub(sub, name, removePrevious=true): 用来绑定组件内进行的所有订阅(rxjs),便于手动取消订阅和组件unmount时自动取消订阅。
  • $unsubscribe(name): 取消绑定在$subs上得订阅,name不传时取消所有订阅,componentWillUnmount时会默认调用此函数来移除所有订阅。
import { gentx } from 'gentx';
import React from 'react';
import { of } from 'rxjs';
import { testFlows } from '../flows/test';

@gentx({
  $bindSub: '$bindSub', // 可以不传,默认`$bindSub`
  $unsubscribe: '$unsubscribe' //可以不传,默认`$unsubscribe`
})
class App extends React.Component {
  constructor() {
    supper();
  }

  componentDidMount() {
    let promise = api.get('xxx');
    let observable = from([1]);

    // flowA, floaB is some flow function in flow/test
    observable = testFlows.floaA(observable);
    observable = testFlows.floaB(observable);

    // 挂载订阅
    let sub1 = observable.subscribe({
      // ...
    });
    this.$bindSub(sub1, 'sub1');

    let sub2 = observable.subscribe({
      // ...
    });
    this.$bindSub(sub2, 'sub2');

    let sub3 = observable.subscribe({
      // ...
    });
    this.$bindSub(sub3, 'sub3');

  }

  // 可以不写,因为默认会执行这个行为
  componentWillUnmount() {
    // 取消所有订阅
    this.$unsubscribe();
  }

  // 点击test按钮
  onClickTest() {
    //...
    // 取消上一次test订阅,并新建一个test订阅
    this.$unsubscribe('test');
    this.bindSub(
      observable.subscribe({
        // ...
      }),
      'test'
    );
  }
}

Vue一起使用

建议:rxjs + gentx + vuex(不用它的action) + vue

为vue提供一个插件VueGentX, 安装插件后,组件实例会多两个属性$bindSub, $unsubscribe

  • $bindSub(sub, name, removePrevious=true): 用来绑定组件内进行的所有订阅(rxjs),便于手动取消订阅和组件beforeDestroy时自动取消订阅。
  • $unsubscribe(name): 取消绑定在$subs上得订阅,name不传时取消所有订阅,beforeDestroy时会默认调用此函数来移除所有订阅。
import Vue from 'vue';
import { VueGentX } from 'gentx';

new Vue({
  el: '#app',
  data() {
    //...
  },

  mounted() {
    let promise = api.get('xxx');
    let observable = from([1]);

    // flowA, floaB is some flow function in flow/test
    observable = testFlows.floaA(observable);
    observable = testFlows.floaB(observable);

    // 挂载订阅
    let sub1 = observable.subscribe({
      // ...
    });
    this.$bindSub(sub1, 'sub1');

    let sub2 = observable.subscribe({
      // ...
    });
    this.$bindSub(sub2, 'sub2');

    let sub3 = observable.subscribe({
      // ...
    });
    this.$bindSub(sub3, 'sub3');
  },

  // 可以不写,因为默认会执行这个行为
  beforeDestroy() {
    // 取消所有订阅
    this.$unsubscribe();
  }

  methods: {
    // 点击test按钮
    onClickTest() {
      //...
      // 取消上一次test订阅,并新建一个test订阅
      this.$unsubscribe('test');
      this.bindSub(
        observable.subscribe({
          // ...
        }),
        'test'
      );
    }
  }
})