Skip to content

fluth

fluth 是一个类 promise 的流式编程库。fluth-vue 默认集成 fluth。

具体使用详情见:fluth

to$

to$ 是一个用于将 Vue 的响应式数据(ref、reactive、computed)转换为 fluth 的 Stream 的工具函数,转换前 vue 响应式数据会进行 deep clone 后再给到 Stream。

类型

typescript
function to$<T>(arg: Ref<T> | ComputedRef<T> | Reactive<T>): Stream<T>;

示例

typescript
import { ref, reactive, computed } from "vue";
import { to$ } from "fluth-vue";

// 转换 ref
const refValue = ref("initial value");
const refStream$ = to$(refValue);

// 转换 reactive 对象
const reactiveObj = reactive({ count: 0, name: "test" });
const reactiveStream$ = to$(reactiveObj);

// 转换 computed
const baseRef = ref(10);
const computedValue = computed(() => baseRef.value * 2);
const computedStream$ = to$(computedValue);

Stream 和 Observable

fluth-vue 对 fluth 的 Stream 和 Observable 进行了增强,增加了 toCompt 和 render$ 方法。

类型

typescript
declare module "fluth" {
  interface Stream<T> extends Readonly<Ref<T>> {
    toCompt: () => ComputedRef<T>;
    render$: (
      renderFn?: (value: T) => VNodeChild | DefineComponent,
    ) => VNodeChild;
  }
  interface Observable<T> extends Readonly<Ref<T | undefined>> {
    toCompt: () => ComputedRef<T | undefined>;
    render$: (
      renderFn?: (value: T | undefined) => VNodeChild | DefineComponent,
    ) => VNodeChild;
  }
}

toCompt

toCompt 方法用于将流的值转换为 computed 对象。

注意

  • toCompt 不能在 template 中直接使用
  • toCompt 在组件的 render 函数中使用的时候,组件的 render 函数必须用 effect$ 包裹,否则流在组件渲染的副作用不会被清理,造成内存泄漏。

注意

toCompt 方法对于 vue 没有版本限制。

vue
<template>
  <div>{{ stream$Compt }}</div>
</template>

<script setup>
import { $ } from "fluth-vue";

const stream$ = $("Hello");
const stream$Compt = stream$.toCompt();
</script>

render$

在组件中使用 render$ 方法会将流的值渲染到新组件中,当流推流时,新组件会自动更新且不会触发组件的 onUpdated 生命周期,推荐在 tsx 中使用:

注意

render$ 在 组件的 render 函数中使用的时候,组件的 render 函数必须用 effect$ 包裹,否则流在组件渲染的副作用不会被清理,造成内存泄漏。

在 setup 中使用定义流的 render$ 方法,可以直接放入组件的 render 函数中

tsx
import { defineComponent, onUpdated } from "vue";
import { $, effect$ } from "fluth-vue";

export default defineComponent(
  () => {
    const user$ = $({ name: "", age: 0, address: "" });
    const order$ = $({ item: "", price: 0, count: 0 });

    onUpdated(() => {
      console.log("Example 组件更新");
    });

    // use$ 推流后只会触发 render$ 的更新,不会触发组件的 onUpdated 生命周期
    const user$Render = user$.render$((v) => (
      <div>
        <div>名字:{v.name}</div>
        <div>年龄:{v.age}</div>
        <div>地址:{v.address}</div>
      </div>
    ));

    // order$ 推流后只会触发 render$ 的更新,不会触发组件的 onUpdated 生命周期
    const order$Render = order$.render$((v) => (
      <div>
        <div>商品:{v.item}</div>
        <div>价格:{v.price}</div>
        <div>数量:{v.count}</div>
      </div>
    ));

    return () => (
      <div>
        <div>用户信息</div>
        {user$Render}
        <div>订单信息</div>
        {order$Render}

        <button onClick={() => user$.set((v) => (v.age += 1))}>
          更新用户信息
        </button>
        <button onClick={() => order$.set((v) => (v.count += 1))}>
          更新订单信息
        </button>
      </div>
    );
  },
  {
    name: "Example",
    props: [],
  },
);

想在 template 中使用流的 render$ 能力,则需要将 script 的 lang 设置为 tsx,并在 setup 中设置定义好流的 render$ 方法,最后通过 <Component :is="" /> 来渲染流。

vue
<template>
  <div>
    <div>用户信息</div>
    <div><Component :is="user$Render" /></div>
    <div>订单信息</div>
    <div><Component :is="order$Render" /></div>

    <button @click="user$.set((v) => (v.age += 1))">更新用户信息</button>
    <button @click="order$.set((v) => (v.count += 1))">更新订单信息</button>
  </div>
</template>

<script setup lang="tsx">
import { $ } from "fluth-vue";
import { onUpdated } from "vue";

const user$ = $({ name: "", age: 0, address: "" });
const order$ = $({ item: "", price: 0, count: 0 });

onUpdated(() => {
  console.log("Example 组件更新");
});

// use$ 推流后只会触发 userRender 的更新,不会触发组件的 onUpdated 生命周期
const user$Render = user$.render$((v) => (
  <div>
    <div>名字:{v.name}</div>
    <div>年龄:{v.age}</div>
    <div>地址:{v.address}</div>
  </div>
));

// order$ 推流后只会触发 orderRender 的更新,不会触发组件的 onUpdated 生命周期
const order$Render = order$.render$((v) => (
  <div>
    <div>商品:{v.item}</div>
    <div>价格:{v.price}</div>
    <div>数量:{v.count}</div>
  </div>
));
</script>

effect$

类型

typescript
function effect$(render: RenderFunction): () => VNodeChild;

effect$ 方法用于清理流在组件 render 函数中渲染的副作用

  • 只有在组件的 render 函数中使用 render$、toCompt 以及所有的流的订阅方法和操作符,才需要使用 effect$ 方法,使用流的响应式数据无需清理。
  • 在 setup 中使用任何方法和流的订阅方法、操作符,无需使用 effect$ 方法。

注意

effect$ 方法只适用于 vue >= 3.2.0 的版本,对于低于 3.2.0 的版本,不建议在组件的 render 函数中使用 render$toCompt以及流的所有订阅方法和操作符。

tsx
import { defineComponent, onUpdated } from "vue";
import { $, effect$ } from "fluth-vue";

export default defineComponent(
  () => {
    const user$ = $({ name: "", age: 0, address: "" });
    const order$ = $({ item: "", price: 0, count: 0 });

    onUpdated(() => {
      console.log("Example 组件更新");
    });

    return effect$(() => (
      <div>
        <div>用户信息</div>
        {/* use$ 推流后只会触发 render$ 内容更新,不会触发组件的 onUpdated 生命周期 */}
        {user$.render$((v) => (
          <div>
            <div>名字:{v.name}</div>
            <div>年龄:{v.age}</div>
            <div>地址:{v.address}</div>
          </div>
        ))}
        <div>订单信息</div>
        {/* order$ 推流后只会触发 render$ 内容更新,不会触发组件的 onUpdated 生命周期 */}
        {order$.render$((v) => (
          <div>
            <div>商品:{v.item}</div>
            <div>价格:{v.price}</div>
            <div>数量:{v.count}</div>
          </div>
        ))}

        <button onClick={() => user$.set((v) => (v.age += 1))}>
          更新用户信息
        </button>
        <button onClick={() => order$.set((v) => (v.count += 1))}>
          更新订单信息
        </button>
      </div>
    );
  },
  {
    name: "Example",
  },
);

recover$

recover$ 方法用于将 vue 的响应式对象转换为 fluth 的 Stream 对象,主要用于将 vue 响应式对象中的流属性转换回 Stream 对象。

注意

由于 vue 的 reactive 作用于对象时,会解构所有对象属性的响应式,因此丢失了属性的类型,无法判断这个属性之前是 Stream 还是 Observable 或者常规属性;recover$ 方法会无差别将 vue 的响应式对象所有属性的 typescript 类型恢复为 fluth 的 Stream 类型

  • 如果之前对象属性是 Stream,则完全正确
  • 如果之前对象属性是 Observable,返回 Steam 是其超集,使用 next 、set 等方法 typescript 不会报错,但运行时会报错
  • 如果之前对象属性是常规属性,禁止使用 recover$ 进行恢复,而应该使用 toRefs 进行恢复

类型

typescript
function recover$<T extends Record<string, any>>(
  reactiveObj: T,
): {
  [K in keyof T]: Stream<T[K]>;
}

示例

typescript
const obj = reactive({
  a$: $("a"),
  b$: $("b"),
});
const { a$, b$ } = recover$(obj);
a$.next("c");
b$.next("d");