ToolJet Set page variable 动作详解:页面级变量的配置、去抖控制与 RunJS 调用
本篇技术指南聚焦 ToolJet 动作系统(Actions)中的 Set page variable(设置页面变量)动作:它用于在 Multipage Apps(多页应用)中为当前页面建立并赋值一个变量。读完后你将掌握该动作的界面配置项(Key、Value、Debounce)及其取值规则、通过 RunJS 查询以 actions.setPageVariable() 编程式触发该动作的语法与参数类型约束,并能从前端源码层面理解页面变量为何“仅限本页可见”、其值存储在何处、如何驱动依赖组件刷新。
1. 什么是页面变量:作用域先于语法
ToolJet 中的变量分为普通变量(regular variables)与页面变量(page variables)两类。二者的核心区别在于作用域:
- 普通变量在整个应用范围内可见,可在任意页面、任意组件的表达式中引用;
- 页面变量被限制在其创建所在的页面(page)内,无法像普通变量那样在整个应用中被访问。
因此,Set page variable 动作的定位是:在 [多页应用] 的某一页内建立一个变量并为其赋值,典型场景包括“页面 A 中用户输入的筛选条件只在本页生效”“页面级别的临时状态(选中行、编辑中的草稿值)随页面生命周期存在,不污染全局变量命名空间”。
从源码结构看,这一作用域限制有明确实现依据。页面变量的读写都携带 moduleId 参数,默认值为 'canvas',读写位置是当前模块(即当前页面/画布)暴露值中的 page.variables 命名空间:
// frontend/src/AppBuilder/_stores/slices/resolvedSlice.js (L264-L278)
setPageVariable: (key, value, moduleId = 'canvas') => {
set(
(state) => {
state.resolvedStore.modules[moduleId].exposedValues.page.variables[key] = value;
},
false,
'setPageVariable'
);
get().updateDependencyValues(`page.variables.${key}`, moduleId);
get().rebuildVariableHints(moduleId);
},
getPageVariable: (key, moduleId = 'canvas') => {
return get().resolvedStore.modules[moduleId].exposedValues.page.variables[key];
},
也就是说,变量值存放在 resolvedStore.modules[moduleId].exposedValues.page.variables[key] 这一按模块隔离的数据结构里,这正是“页面变量不能跨页访问”的底层机制。赋值完成后还会调用 updateDependencyValues 与 rebuildVariableHints,保证引用了 page.variables.<key> 的组件表达式被重新求值、代码提示同步更新。
此外,脚本分析工具 也把页面变量访问归类为独立的读写类别,用于追踪脚本对页面变量的依赖:
// frontend/src/AppBuilder/_utils/scriptAnalysis.ts (L35-L37)
['setPageVariable', 'pageVariableWrites'],
['unsetPageVariable', 'pageVariableWrites'],
['getPageVariable', 'pageVariableReads'],
2. 动作定义与界面配置项
Set page variable 在动作类型表中定义为 set-page-variable,归属于 variable 分组,包含两个 code 类型选项,默认均为空字符串:
// frontend/src/AppBuilder/RightSideBar/Inspector/ActionTypes.js (L91-L99)
{
name: 'Set page variable',
id: 'set-page-variable',
options: [
{ name: 'key', type: 'code', default: '' },
{ name: 'value', type: 'code', default: '' },
],
group: 'variable',
},
在组件的事件管理器(Event Manager)右侧面板中,选择该动作后会渲染 Key 与 Value 两个 CodeHinter 输入框,两者均支持 fx 表达式语法,可在输入时预览解析结果:
// frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx (L921-L942)
{event.actionId === 'set-page-variable' && (
<>
<FieldRow label={t('editor.inspector.eventManager.key', 'Key')}>
<CodeHinter type="basic" initialValue={event.key}
onChange={(value) => handlerChanged(index, 'key', value)}
enablePreview={true} component={component} />
</FieldRow>
<FieldRow label={t('editor.inspector.eventManager.value', 'Value')}>
<CodeHinter type="basic" initialValue={event.value}
onChange={(value) => handlerChanged(index, 'value', value)}
component={component} />
</FieldRow>
</>
)}
2.1 两个核心字段
| 字段 | 说明 | 取值示例 |
|---|---|---|
| Key | 变量键名,保存为 page.variables.<key> 中的属性名 |
selectedRow、filterValue |
| Value | 变量值,可为字面量或 fx 表达式,执行时按当前上下文解析 | {{query1.data[0]}}、true、300 |
2.2 Debounce(去抖)字段
按官方文档说明:debounce 字段默认为空;可以输入一个数值(毫秒),表示延迟多久后才真正执行动作,例如 300。该字段是所有事件动作共有的通用配置,在 EventManager 面板底部以 CodeHinter 呈现:
// frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx (L1117-L1126)
<FieldRow label={t('editor.inspector.eventManager.debounce', 'Debounce')} dataCy="debounce-label">
<CodeHinter
type="basic"
initialValue={event.debounce}
onChange={(value) => handlerChanged(index, 'debounce', value)}
usePortalEditor={false}
component={component}
/>
</FieldRow>
从源码结构看,输入为空时 debounce 键会被直接从事件对象中移除,保持事件定义干净:
// frontend/src/AppBuilder/RightSideBar/Inspector/EventManager.jsx (L384-L387)
// Remove debounce key if it's empty
if (param === 'debounce' && value === '') {
delete updatedEvent.event.debounce;
}
实战提示:debounce 最适合配合“连续触发”的场景,例如在文本输入事件中设置页面变量——输入过程中事件高频触发,设置 300 可让动作在停止输入 300ms 后才执行一次,避免频繁的变量写入与依赖组件重算。
3. 执行链路:从事件到状态更新
当绑定了 Set page variable 动作的组件事件触发时,前端事件切片会进入对应分支:先对 Key 与 Value 做表达式解析(getResolvedValue 会代入当前上下文中的自定义变量),再调用状态更新:
// frontend/src/AppBuilder/_stores/slices/eventsSlice.js (L861-L866)
case 'set-page-variable': {
const { setPageVariable } = get();
const key = getResolvedValue(event.key, customVariables, moduleId);
const value = getResolvedValue(event.value, customVariables, moduleId);
setPageVariable(key, value, moduleId);
return Promise.resolve();
}
完整调用链可以概括为:
- 组件事件触发(如
onClick、onChange); eventsSlice的executeAction分发到set-page-variable分支,解析 key/value;- 调用
resolvedSlice.setPageVariable(key, value, moduleId),把值写入该模块的exposedValues.page.variables; updateDependencyValues('page.variables.<key>')触发依赖该变量路径的组件表达式重算,UI 即时刷新。
4. 通过 RunJS 查询触发:actions.setPageVariable()
除界面事件绑定外,官方文档提供了等价的编程方式——在 RunJS 查询中调用动作 API:
await actions.setPageVariable('<variablekey>', <variablevalue>)
参数类型规则:variablekey 必须以字符串形式给出(带引号),variablevalue 若是数值则不需要引号。例如:
await actions.setPageVariable('currentStep', 3)
await actions.setPageVariable('note', 'first draft')
从源码结构看,actions.setPageVariable 并不是旁路函数,而是构造出一个与界面事件完全同构的虚拟动作事件再走同一套执行管线,这保证了两种触发方式行为一致(包括依赖刷新):
// frontend/src/AppBuilder/_stores/slices/eventsSlice.js (L1312-L1334)
const setPageVariable = (key = '', value = '') => {
const event = {
actionId: 'set-page-variable',
key,
value,
// ...
};
// ...
return executeAction(event, mode, {});
};
setPageVariable 随 actions 对象一同暴露给 RunJS 环境,并在动作常量清单中注册:
// frontend/src/AppBuilder/_stores/slices/eventsSlice.js (L1470-L1473)
setPageVariable,
getPageVariable,
unsetAllPageVariables,
unsetPageVariable,
// frontend/src/AppBuilder/_stores/constants/actions.js (L17-L19)
'setPageVariable',
'unsetAllPageVariables',
'unsetPageVariable',
关于“如何从 RunJS 查询中运行动作”的完整操作步骤,可参考文档站中的 how-to 指南《Running Actions from RunJS Query》。
5. 相关动作与配套能力
Set page variable 属于页面变量动作族的一员,在动作列表中与之并列的还有:
- Unset page variable(
unset-page-variable):按 key 删除当前页面的单个变量,内部实现为删除exposedValues.page.variables[key]并移除对应的依赖节点; - Unset all page variables(
unset-all-page-variables):清空当前页面的全部页面变量; - getPageVariable(key):在 RunJS 中读取当前页面的变量值,签名同样携带
moduleId = 'canvas'默认参数。
由于 unsetPageVariable 会执行 removeNode 并刷新依赖(见 resolvedSlice.js L279-L288),配合 Set page variable 使用时,页面状态可以“写入—读取—清理”完整闭环,而不会把过期的页面变量泄漏到其他页面。
6. 小结与适用前提
- 适用前提:Set page variable 面向多页应用(Multipage Apps)场景;如果你只构建单页应用且不关心作用域隔离,使用普通变量(Set variable)通常更直接。
- 配置要点回顾:Key/Value 均为支持 fx 表达式的 code 输入;Debounce 默认留空,填写毫秒数(如
300)可延迟执行,适合高频事件节流。 - 两种等价触发方式:事件管理器绑定
set-page-variable,或 RunJS 中await actions.setPageVariable(key, value),二者走同一条executeAction管线,行为一致。 - 作用域约束:变量值按模块(页面)隔离存储于
exposedValues.page.variables,这是“页面变量不可跨页访问”的源码级依据;引用该变量的组件在赋值后会通过依赖机制自动刷新。
关键源码索引:动作定义见 ActionTypes.js,界面字段见 EventManager.jsx,执行分发见 eventsSlice.js,存储与依赖刷新见 resolvedSlice.js。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

