flowgram.ai/apps/docs/src/zh/guide/advanced/variable/variable-consume.mdx
2025-07-04 08:51:07 +00:00

252 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 消费变量
在 Flowgram 中,变量的管理和消费是构建动态、可交互应用的核心。理解如何有效地消费变量,对于开发者来说至关重要。这篇文档将带你深入了解在不同场景下消费变量的各种方式。
## 在节点内获取变量树
在画布的节点中,我们常常需要获取当前作用域下可用的变量,并将它们以树形结构展示出来,方便用户进行选择和操作。`useAvailableVariables` 这个 React Hook 就是为此而生。
### `useAvailableVariables`
`useAvailableVariables` 是一个轻量级的 Hook它直接返回当前作用域可用的变量数组 (`VariableDeclaration[]`)。如果你只需要一个简单的变量列表,并且不需要进行更复杂的操作,那么 `useAvailableVariables` 会是更便捷的选择。
```tsx pure title="use-variable-tree.tsx"
import {
type BaseVariableField,
useAvailableVariables,
} from '@flowgram.ai/fixed-layout-editor';
// .... 在 React 组件或 Hook 中
const availableVariables = useAvailableVariables();
const renderVariable = (variable: BaseVariableField) => {
// 这里可以根据你的需求渲染每个变量
// ....
}
return availableVariables.map(renderVariable);
// ....
```
### 获取 Object 类型变量的下钻
当变量的类型是 `Object` 时,我们往往需要能够“下钻”到它的内部,获取其属性。`ASTMatch.isObject` 方法可以帮助我们判断一个变量类型是否为对象。如果是,我们就可以递归地渲染它的 `properties`。
```tsx pure title="use-variable-tree.tsx"
import {
type BaseVariableField,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
// 只有 Object 类型的变量才可以下钻
children: ASTMatch.isObject(variable.type) ? variable.type.properties.map(renderVariable) : [],
});
// ....
```
### 获取 Array 类型变量的下钻
与 `Object` 类型类似,当遇到 `Array` 类型的变量时,我们也希望能展示它的内部结构。对于数组,我们通常关心的是其元素的类型。`ASTMatch.isArray` 可以判断变量类型是否为数组。值得注意的是,数组的元素类型可能是任意的,甚至可能是另一个数组。因此,我们需要一个递归的辅助函数 `getTypeChildren` 来处理这种情况。
```tsx pure title="use-variable-tree.tsx"
import {
type BaseVariableField,
type BaseType,
ASTMatch,
} from '@flowgram.ai/fixed-layout-editor';
// ....
const getTypeChildren = (type?: BaseType): BaseVariableField[] => {
if (!type) return [];
// 获取 Object 的属性
if (ASTMatch.isObject(type)) return type.properties;
// 递归获取 Array 的元素类型
if (ASTMatch.isArray(type)) return getTypeChildren(type.items);
return [];
};
const renderVariable = (variable: BaseVariableField) => ({
title: variable.meta?.title,
key: variable.key,
children: getTypeChildren(variable.type).map(renderVariable),
});
// ....
```
## 官方物料:`VariableSelector`
为了让你能更轻松地在应用中集成变量选择的功能,我们为你准备了官方物料 —— `VariableSelector` 组件。它封装了前面提到的所有逻辑,让你无需从头开始,即可拥有一个功能强大、界面美观的变量选择器。
<img loading="lazy" src="/materials/variable-selector.png" style={{width:500}}/>
`VariableSelector` 不仅支持展示变量树,还内置了搜索、过滤等高级功能,可以极大地提升用户体验。更详细的介绍,请参考 [官方表单物料](/guide/advanced/form-materials.html) 文档。
你可以通过以下两种方式来使用它:
**1. 通过 NPM 包引用**
这是最简单直接的方式,只需一行代码,即可在你的项目中引入 `VariableSelector`。
```tsx
import { VariableSelector } from '@flowgram.ai/form-materials';
```
**2. 通过 CLI 复制源代码**
如果你希望对 `VariableSelector` 进行更深度的定制,我们也提供了通过 CLI 将组件源代码直接复制到你的项目中的方式。这样,你就可以随心所欲地修改它,以满足你独特的业务需求。
```bash
npx @flowgram.ai/materials components/variable-selector
```
## `ScopeAvailableData`:你的变量百宝箱
`ScopeAvailableData` 对象是变量系统的核心之一,它由 `useScopeAvailable` Hook 返回,是你与作用域内可用变量进行交互的主要桥梁。你可以把它想象成一个功能强大的“变量工具箱”。
### `useScopeAvailable`
`useScopeAvailable` 是一个功能更强大的 Hook它能够返回一个 `ScopeAvailableData` 对象,其中不仅包含了当前作用域所有可用的变量信息,还提供了一些高级 API比如 `trackByKeyPath`。
它与 `useAvailableVariables` 的主要区别在于:
* **返回值不同**`useAvailableVariables` 直接返回变量数组,而 `useScopeAvailable` 返回的是一个包含了 `variables` 属性以及其他方法的 `ScopeAvailableData` 对象。
* **适用场景**:当你需要对变量进行更复杂的操作,比如追踪单个变量的变化时,`useScopeAvailable` 是你的不二之选。
```tsx
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
const available = useScopeAvailable();
// available 对象上包含了变量列表和其他 API
console.log(available.variables);
```
### 获取变量列表
最基础的用法就是获取当前作用域下所有可用的变量。
```tsx
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
function MyComponent() {
const available = useScopeAvailable();
// available.variables 就是一个包含了所有可用变量的数组
console.log(available.variables);
return (
<ul>
{available.variables.map(variable => (
<li key={variable.key}>{variable.name}</li>
))}
</ul>
);
}
```
### 追踪单个变量的变化:`trackByKeyPath`
当你只关心某个特定变量(尤其是嵌套在 Object 或 Array 中的变量)的变化时,`trackByKeyPath` 就派上用场了。它能让你精准地“订阅”这个变量的更新,而不会因为其他不相关变量的变化导致组件重新渲染,从而实现更精细的性能优化。
假设我们有一个名为 `user` 的 Object 类型变量,它有一个 `name` 属性。我们希望在 `user.name` 变化时更新组件。
```tsx
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect, useState } from 'react';
function UserNameDisplay() {
const available = useScopeAvailable();
const [userName, setUserName] = useState('');
useEffect(() => {
// 定义我们要追踪的变量路径
const keyPath = ['user', 'name'];
// 开始追踪!
const disposable = available.trackByKeyPath(keyPath, (nameField) => {
// 当 user.name 变量字段变化时,这个回调函数会被触发
// nameField 就是那个变化的变量字段,我们可以从中获取最新的默认值
setUserName(nameField?.meta.default || '');
});
// 组件卸载时,别忘了取消追踪,避免内存泄漏
return () => disposable.dispose();
}, [available]); // 依赖项是 available 对象
return <div>User Name: {userName}</div>;
}
```
### 高级事件监听 API
除了 `trackByKeyPath``ScopeAvailableData` 还提供了一套更底层的事件监听 API让你能够更精细地控制变量变化的响应逻辑。这在处理一些复杂的、需要手动管理订阅的场景时非常有用。
下面我们通过一个表格来详细对比这三个核心的监听 API
| API | 触发时机 | 回调参数 | 核心区别与适用场景 |
| :--- | :--- | :--- | :--- |
| `onVariableListChange` | 当可用变量的**列表结构**发生变化时。 | `(variables: VariableDeclaration[]) => void` | **只关心列表本身**。比如,上游节点新增/删除了一个输出变量,导致可用变量的总数或成员发生了变化。它不关心变量内部和下钻的改变。适用于需要根据变量列表的有无或数量来更新 UI 的场景。 |
| `onAnyVariableChange` | 当列表中**任意一个**变量的**类型,元数据和下钻字段**发生变化时。 | `(changedVariable: VariableDeclaration) => void` | **只关心变量内容的更新**。比如,用户修改了一个输出变量的类型。它不关心列表结构的变化。适用于需要对任何一个变量的内容变化做出反应的场景。 |
| `onListOrAnyVarChange` | 以上两种情况**任意一种**发生时。 | `(variables: VariableDeclaration[]) => void` | **最全面的监听**,是前两者的结合。无论是列表结构变化,还是任何一个变量的变化,都会触发。适用于需要对任何可能的变化都进行响应的“兜底”场景。 |
#### 代码示例
让我们通过一个具体的例子来看看如何在组件中使用这些 API。
```tsx
import { useScopeAvailable } from '@flowgram.ai/fixed-layout-editor';
import { useEffect } from 'react';
function AdvancedListenerComponent() {
const available = useScopeAvailable();
useEffect(() => {
// 1. 监听列表结构变化
const listChangeDisposable = available.onVariableListChange((variables) => {
console.log('可用变量列表的结构变了!新的列表长度是:', variables.length);
});
// 2. 监听任意变量的变化
const valueChangeDisposable = available.onAnyVariableChange((changedVariable) => {
console.log(`变量 '${changedVariable.keyPath.join('.')}' 的类型、下钻或者 meta 变了`);
});
// 3. 监听所有变化(结构或单个变量内部)
const allChangesDisposable = available.onListOrAnyVarChange((variables) => {
console.log('变量列表或其中某个变量发生了变化!');
// 注意:这里的回调参数是完整的变量列表,而不是单个变化的变量
});
// 在组件卸载时,务必清理所有的监听器,防止内存泄漏
return () => {
listChangeDisposable.dispose();
valueChangeDisposable.dispose();
allChangesDisposable.dispose();
};
}, [available]);
return <div>请在控制台查看变量变化的日志...</div>;
}
```
**关键点**
* 这些 API 返回的都是一个 `Disposable` 对象。
* 为了避免内存泄漏和不必要的计算,你必须在 `useEffect` 的清理函数中调用其 `dispose()` 方法来取消监听。