Skip to content

Commit 28a7e4d

Browse files
committed
feat: add render$ directive
1 parent fa1caef commit 28a7e4d

18 files changed

+5052
-95
lines changed

packages/.vitepress/config.cn.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default {
3232
link: "/cn/useFluth/index.html",
3333
items: [
3434
{ text: "to$", link: "/cn/useFluth/to$.html" },
35+
{ text: "render$", link: "/cn/useFluth/render$.html" },
3536
{ text: "toComp", link: "/cn/useFluth/toComp.html" },
3637
{ text: "toComps", link: "/cn/useFluth/toComps.html" },
3738
],

packages/.vitepress/config.en.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default {
3232
link: "/en/useFluth/index.html",
3333
items: [
3434
{ text: "to$", link: "/en/useFluth/to$.html" },
35+
{ text: "render$", link: "/en/useFluth/render$.html" },
3536
{ text: "toComp", link: "/en/useFluth/toComp.html" },
3637
{ text: "toComps", link: "/en/useFluth/toComps.html" },
3738
],

packages/core/useFluth/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
isRef,
1313
isReactive,
1414
toRaw,
15+
DirectiveBinding,
16+
Directive,
1517
} from "vue-demi";
1618

1719
export * from "fluth";
@@ -109,6 +111,28 @@ export function to$<T>(
109111
return stream$;
110112
}
111113

114+
/**
115+
* vue directive, convert stream value to dom element content
116+
*/
117+
export const render$: Directive = {
118+
mounted(el: HTMLElement, binding: DirectiveBinding) {
119+
const stream$ = binding.value;
120+
if (!(stream$ instanceof Stream) && !(stream$ instanceof Observable)) {
121+
throw new Error("$render only accepts Stream or Observable as input");
122+
}
123+
124+
const observable$ = stream$.thenImmediate((v) => {
125+
el.textContent = v?.toString() ?? "";
126+
});
127+
128+
(el as any).__fluth_unsubscribe = () => observable$.unsubscribe();
129+
},
130+
beforeUnmount(el: HTMLElement) {
131+
(el as any).__fluth_unsubscribe?.();
132+
(el as any).__fluth_unsubscribe = null;
133+
},
134+
};
135+
112136
/**
113137
* create stream factory with default plugin
114138
*/
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# render$ 指令
2+
3+
`render$` 是一个Vue指令,用于将 Stream 或 Observable 的值动态渲染到DOM元素的文本内容中。
4+
::: info 注意
5+
采用指令渲染流不会引起组件的更新,只会触发元素的更新,类似于[signals](https://github.com/tc39/proposal-signals)
6+
:::
7+
8+
## 语法
9+
10+
```vue
11+
<div v-render$="stream$"></div>
12+
```
13+
14+
## 示例
15+
16+
### Stream
17+
18+
```vue
19+
<template>
20+
<div>
21+
<div v-render$="message$"></div>
22+
<button @click="updateMessage">更新消息</button>
23+
</div>
24+
</template>
25+
26+
<script setup>
27+
import { $, render$ } from "fluth-vue";
28+
29+
const vRender$ = render$;
30+
31+
const message$ = $("Hello World");
32+
33+
const updateMessage = () => {
34+
message$.next("消息已更新!");
35+
};
36+
</script>
37+
```
38+
39+
### Observable
40+
41+
```vue
42+
<template>
43+
<div>
44+
<div v-render$="processedData$"></div>
45+
<button @click="updateData">更新数据</button>
46+
</div>
47+
</template>
48+
49+
<script setup>
50+
import { $, render$ } from "fluth-vue";
51+
52+
const vRender$ = render$;
53+
54+
const rawData$ = $("原始数据");
55+
const processedData$ = rawData$.then((data) => `处理后的: ${data}`);
56+
57+
const updateData = () => {
58+
rawData$.next("新数据");
59+
};
60+
</script>
61+
```
62+
63+
### 链式操作
64+
65+
```vue
66+
<template>
67+
<!-- ✅ Good performance, only object$.value.attr.name change will trigger re-render -->
68+
<div v-render$="object$.pipe(get((v) => v.attr.name))"></div>
69+
70+
<!-- ❌ bad performance, object$ change will trigger re-render, but attr name change will not -->
71+
<div v-render$="object$.then((v) => v.attr.name)"></div>
72+
</template>
73+
74+
<script setup>
75+
import { $, get, render$ } from "fluth-vue";
76+
77+
const vRender$ = render$;
78+
79+
const object$ = $({ id: 1, attr: { name: "fluth", age: 18 } });
80+
</script>
81+
```
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# render$ Directive
2+
3+
The `render$` directive is a Vue directive that dynamically renders Stream or Observable values to DOM element text content.
4+
5+
::: info Note
6+
Using the directive to render streams will not trigger component updates, only trigger element updates, similar to [signals](https://github.com/tc39/proposal-signals)
7+
:::
8+
9+
## Syntax
10+
11+
```vue
12+
<div v-render$="stream$"></div>
13+
```
14+
15+
## Examples
16+
17+
### Stream
18+
19+
```vue
20+
<template>
21+
<div>
22+
<div v-render$="message$"></div>
23+
<button @click="updateMessage">Update Message</button>
24+
</div>
25+
</template>
26+
27+
<script setup>
28+
import { $, render$ } from "fluth-vue";
29+
30+
const vRender$ = render$;
31+
32+
const message$ = $("Hello World");
33+
34+
const updateMessage = () => {
35+
message$.next("Message updated!");
36+
};
37+
</script>
38+
```
39+
40+
### Observable
41+
42+
```vue
43+
<template>
44+
<div>
45+
<div v-render$="processedData$"></div>
46+
<button @click="updateData">Update Data</button>
47+
</div>
48+
</template>
49+
50+
<script setup>
51+
import { $, render$ } from "fluth-vue";
52+
53+
const vRender$ = render$;
54+
55+
const rawData$ = $("raw data");
56+
const processedData$ = rawData$.then((data) => `Processed: ${data}`);
57+
58+
const updateData = () => {
59+
rawData$.next("new data");
60+
};
61+
</script>
62+
```
63+
64+
### Chained Operations
65+
66+
```vue
67+
<template>
68+
<!-- ✅ Good performance, only object$.value.attr.name change will trigger re-render -->
69+
<div v-render$="object$.pipe(get((v) => v.attr.name))"></div>
70+
71+
<!-- ❌ bad performance, object$ change will trigger re-render, but attr name change will not -->
72+
<div v-render$="object$.then((v) => v.attr.name)"></div>
73+
</template>
74+
75+
<script setup>
76+
import { $, get, render$ } from "fluth-vue";
77+
78+
const vRender$ = render$;
79+
80+
const object$ = $({ id: 1, attr: { name: "fluth", age: 18 } });
81+
</script>
82+
```

0 commit comments

Comments
 (0)