Skip to content

Streaming Rendering

In addition to using stream reactive data for rendering, fluth provides powerful streaming rendering render$ functionality that can achieve element-level rendering or block-level rendering, with overall effects similar to signal or block signal rendering.

Element-level Rendering

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

export default defineComponent(
  () => {
    const name$ = $("hello");

    onUpdated(() => {
      console.log("Example component updated");
    });

    return effect$(() => (
      <div>
        <div>
          Name: {name$.render$()}
        </div>
        <button onClick={() => name$.set((v) => v + " world")}>Update</button>
      </div>
    );
  },
  {
    name: "Example",
    props: [],
  },
);

When clicking the update button to update the text, the component's onUpdated lifecycle will not be triggered.

Block-level Rendering

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

export default defineComponent(
  () => {
    const info$ = $({ name: "", age: 0, address: "" });

    onUpdated(() => {
      console.log("Example component updated");
    });

    return effect$(() => (
      <div>
        <div>User Information</div>
        {user$.render$((v) => (
          <div>
            <div>Name: {v.name}</div>
            <div>Age: {v.age}</div>
            <div>Address: {v.address}</div>
          </div>
        ))}

        <button onClick={() => user$.set((v) => (v.age += 1))}>
          Update User Information
        </button>
      </div>
    );
  },
  {
    name: "Example",
  },
);

Whether it's user information or order information, after clicking the update button, the component's onUpdated lifecycle will not be triggered.

Comparison

Comparison between fluth render$ and ref rendering:

tsx
/* eslint-disable @typescript-eslint/no-unused-vars */
import { defineComponent, onUpdated, h } from "vue";
import { $, effect$ } from "fluth-vue";

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

    return effect$(() => (
      <div class="card-light">
        <div> example component </div>
        <div>render time: {Date.now()}</div>
        <section style={{ display: "flex", justifyContent: "space-between" }}>
          {/* use$ emit data only trigger render$ content update*/}
          {user$.render$((v) => (
            <div key={Date.now()} class="card">
              <div>user$ render</div>
              <div>name:{v.name}</div>
              <div>age:{v.age}</div>
              <div>address:{v.address}</div>
              <div>render time: {Date.now()}</div>
            </div>
          ))}
          {/* order$ emit data only trigger render$ content update*/}
          {order$.render$((v) => (
            <div key={Date.now()} class="card">
              <div>order$ render</div>
              <div>item:{v.item}</div>
              <div>price:{v.price}</div>
              <div>count:{v.count}</div>
              <div>render time: {Date.now()}</div>
            </div>
          ))}
        </section>

        <div class="operator">
          <button class="button" onClick={() => user$.set((v) => (v.age += 1))}>
            update user$ age
          </button>
          <button
            class="button"
            onClick={() => order$.set((v) => (v.count += 1))}
          >
            update order$ count
          </button>
        </div>
      </div>
    ));
  },
  {
    name: "streamingRender",
  },
);
tsx
/* eslint-disable @typescript-eslint/no-unused-vars */
import { defineComponent, ref, h } from "vue";
import { $, effect$ } from "fluth-vue";

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

    return effect$(() => (
      <div class="card-light" key={Date.now()}>
        <div> example component </div>
        <div>render time: {Date.now()}</div>
        <section style={{ display: "flex", justifyContent: "space-between" }}>
          <div key={Date.now()} class="card">
            <div>user ref render</div>
            <div>name:{user.value.name}</div>
            <div>age:{user.value.age}</div>
            <div>address:{user.value.address}</div>
            <div>render time: {Date.now()}</div>
          </div>
          <div key={Date.now()} class="card">
            <div>order ref render</div>
            <div>item:{order.value.item}</div>
            <div>price:{order.value.price}</div>
            <div>count:{order.value.count}</div>
            <div>render time: {Date.now()}</div>
          </div>
        </section>

        <div class="operator">
          <button class="button" onClick={() => (user.value.age += 1)}>
            update user age
          </button>
          <button class="button" onClick={() => (order.value.count += 1)}>
            update order count
          </button>
        </div>
      </div>
    ));
  },
  {
    name: "RefRender",
  },
);
tsx
/* eslint-disable @typescript-eslint/no-unused-vars */
import { defineComponent, ref, h } from "vue";
import { $, effect$ } from "../../core/useFluth/index";

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

    return effect$(() => (
      <div class="card-light" key={Date.now()}>
        <div> example component </div>
        <div>render time: {Date.now()}</div>
        <section style={{ display: "flex", justifyContent: "space-between" }}>
          {/* use$ emit data only trigger render$ content update*/}
          {user$.render$((v) => (
            <div key={Date.now()} class="card">
              <div>user$ render</div>
              <div>name:{v.name}</div>
              <div>age:{v.age}</div>
              <div>address:{v.address}</div>
              <div>render time: {Date.now()}</div>
            </div>
          ))}
          <div key={Date.now()} class="card">
            <div>order ref render</div>
            <div>item:{order.value.item}</div>
            <div>price:{order.value.price}</div>
            <div>count:{order.value.count}</div>
            <div>render time: {Date.now()}</div>
          </div>
        </section>

        <div class="operator">
          <button class="button" onClick={() => user$.set((v) => (v.age += 1))}>
            update user$ age
          </button>
          <button class="button" onClick={() => (order.value.count += 1)}>
            update order count
          </button>
        </div>
      </div>
    ));
  },
  {
    name: "MixRender",
  },
);

Streaming Rendering Effect

example component
render time: 1756111673833
user$ render
name:
age:0
address:
render time: 1756111673833
order$ render
item:
price:0
count:0
render time: 1756111673833

Ref Rendering Effect

example component
render time: 1756111673833
user ref render
name:
age:0
address:
render time: 1756111673833
order ref render
item:
price:0
count:0
render time: 1756111673833

Streaming + Ref Mixed Rendering Effect

example component
render time: 1756111673833
user$ render
name:
age:0
address:
render time: 1756111673833
order ref render
item:
price:0
count:0
render time: 1756111673833