Shared state (namespace)
默认每个
```interact块独立持有自己的 state——拖一个块的滑块不影响别的块。 用namespace <name>让多个 interact 块共享同一组变量——一次声明 sliders / computed,多块复用。
When to use
- ✅ 同一组 sliders 驱动多块输出(例:3 个滑块控本金/利率/年限,文本块 + chart 块都用)
- ✅ 正文穿插的多 chart(写一段、放一个 chart、写一段、再一个 chart——全用同一组参数)
- ✅ Layout / Tabs 内多 cards 共享数据(左卡 sliders,右卡 chart)
- ✅ 避免重复声明(5 个 interact 块都要同一个 slider,写 5 次维护噩梦)
- ❌ 真隔离的两个独立计算器(两个 BMI 计算器互不干扰,不要同 namespace)
- ❌ 用作"全局变量库"(namespace 应该聚焦单一上下文,别放进所有变量)
Basic syntax
加 namespace <name> 在 ```interact 后:
\```interact namespace finance
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
\```
正文段落...
\```interact namespace finance
template stl:
[利率] -> [{(rate*100):.1f}%]
[年限] -> [{years} 年]
\```
渲染:第 1 块出现 2 个 sliders;第 2 块没有 sliders(它只复用 namespace 里的),只有 template 输出。拖 1 块 slider → 2 块的输出实时更新。
namespace 命名规则
- ASCII 字母 + 数字 + 下划线
- 大小写敏感(
finance≠Finance) - 推荐用语义名(
finance/physics/bmi/chart_demo) - 跨页面不共享——namespace 只在单页有效(关 page state 就丢)
Examples
Example 1:参数 + 输出 + chart 三块共用
**复利计算器** —— 拖滑块看变化。
\```interact namespace finance
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
computed compound = principal * pow(1+rate, years)
computed gain = compound - principal
\```
数字结果:
\```interact namespace finance
template stl:
[本金] -> [${principal:.0f}]
[终值] -> [${compound:.0f}]
[净收益] -> [${gain:.0f}]
\```
增长曲线:
\```interact namespace finance
template vega-lite:
{
"data": {"sequence": {"start": 0, "stop": {years}, "step": 1, "as": "year"}},
"transform": [{"calculate": "{principal} * pow(1+{rate}, datum.year)", "as": "value"}],
"mark": "line",
"encoding": {"x": {"field": "year"}, "y": {"field": "value", "type": "quantitative"}}
}
\```
3 个块共用 finance namespace。第 1 块定义 sliders + computed;第 2/3 块只是 template,复用所有变量。拖第 1 块 slider 同步驱动第 2/3 块。
Example 2:Layout + namespace 配合
\```layout grid cols=2
:::card accent=blue
**输入**
\```interact namespace bmi
slider weight 40 120 70 1
slider height 1.4 2.1 1.7 0.01
computed bmi_value = weight / (height * height)
template stl:
[体重] -> [{weight} kg]
[身高] -> [{height:.2f} m]
\```
:::
:::card accent=green
**结果**
\```interact namespace bmi
template stl:
[BMI] -> [{bmi_value:.1f}]
[分类] -> [{bmi_value < 18.5 ? "偏瘦" : bmi_value < 24 ? "正常" : "超重"}]
\```
:::
\```
Layout 提供视觉布局,namespace 提供 state 共享——两者正交合作。
Example 3:跨章节共用一组参数
## 财务模拟
我们看一个简单的退休规划。先设定参数:
\```interact namespace retire
slider currentAge 20 60 30 1
slider retireAge 50 75 65 1
slider monthly 100 5000 1000 100
slider annualRate 0 0.1 0.05 0.005
computed years = retireAge - currentAge
computed total = monthly * 12 * years * pow(1 + annualRate/12, 12*years)
\```
### 数字结果
\```interact namespace retire
template stl:
[当前年龄] -> [{currentAge}]
[退休年龄] -> [{retireAge}]
[投资期] -> [{years} 年]
[退休时本金] -> [${total:.0f}]
\```
### 资金成长
\```interact namespace retire
template vega-lite: {...}
\```
### 关键洞察
可以看到,把 `monthly` 调高 ¥100,退休本金涨 ${...}(实时)。
**早开始** + **持续投** > 短期内多投。
整个章节 4 个交互块共用同一 namespace,读者拖一处 slider 整章数字 + chart + 推论全部更新。
Example 4:多个独立 namespace(隔离)
**比较两个投资方案** —— A 和 B 互不干扰。
A 方案:
\```interact namespace planA
slider rate 0 0.15 0.05 0.01
slider years 1 30 10 1
template stl:
[A 终值] -> [${1000 * pow(1+rate, years):.0f}]
\```
B 方案:
\```interact namespace planB
slider rate 0 0.15 0.08 0.01
slider years 1 30 15 1
template stl:
[B 终值] -> [${1000 * pow(1+rate, years):.0f}]
\```
读者可以同时看到两个方案在不同参数下的结果。
两个 namespace 隔离——A 块的 rate 跟 B 块的 rate 完全独立。
Plain-text fallback behavior
namespace 在不支持 reader 里显示为代码块字面量——读者看到 \```interact namespace finance,理解原文档想跨块共享。
\```interact namespace finance
slider rate 0 0.15 0.05 0.01
\```
| Reader | 渲染效果 |
|---|---|
| Rho | 完整跨块 state 共享 |
| GitHub web | 代码块(namespace 字面量可见) |
| Obsidian | 同上 |
| VS Code 默认预览 | 同上 |
| cat / less | plain text |
namespace 名称在源文件里自文档化——读者看到 namespace finance 就知道相关块属于同一 context。
Common pitfalls
1. 忘加 namespace → state 隔离
\```interact
slider rate 0 0.15 0.05 0.01
\```
\```interact
template stl: [利率] -> [{rate}] ← ❌ 第 2 块的 rate 未声明
\```
→ 第 2 块抛 "undefined rate" 或显示 {rate} 字面量。两块都加同一 namespace 才能共享:
\```interact namespace foo
slider rate 0 0.15 0.05 0.01
\```
\```interact namespace foo
template stl: [利率] -> [{rate}] ← ✅
\```
2. namespace 大小写不一致
\```interact namespace Finance
slider rate ...
\```
\```interact namespace finance ← ❌ 大小写不同 → 不共享
template ...
\```
→ namespace 大小写敏感。Finance ≠ finance。
3. 同 namespace 重复声明同变量
\```interact namespace foo
slider rate 0 0.15 0.05 0.01
\```
\```interact namespace foo
slider rate 0 0.20 0.10 0.05 ← ❌ 重复声明 rate
\```
→ 第 2 次声明行为未定义(可能覆盖、可能报错、可能保留第一次)。namespace 内每个变量只声明一次——后续块只引用,不重声明。
4. namespace 滥用 → 全部全局
\```interact namespace shared
slider a 0 10 5 1
slider b 0 10 5 1
slider c 0 10 5 1
... (40 个 sliders)
\```
→ 单 namespace 装太多 → 读者无法理解哪块跟哪块相关。一个 namespace 应聚焦单一上下文(一个计算器 / 一个章节的参数面板)。多 context 用多 namespace。
5. 跨页面期待持久化
<!-- 在 page A: -->
\```interact namespace foo
slider x 0 10 5 1
\```
<!-- 在 page B: -->
\```interact namespace foo
template stl: [{x}] ← ❌ x 未在 B 声明,namespace 不跨页
\```
→ namespace 只在单页有效——跳到 B page 时,A 的 foo namespace 没了。每页 namespace 独立。
6. namespace 跟外部 JS 混用
\```interact namespace foo
slider rate 0 0.15 0.05 0.01
\```
<script>
// 想从 JS 读 foo.rate
</script>
→ namespace state 不暴露给外部 JS——它在 Rho hydration 内部管理。Rho 不是 JS framework——要 JS 集成走 Developer Reference 的 hydration API。
7. 嵌套不能突破 namespace 隔离
\```layout
:::card
\```interact namespace foo
slider x 0 10 5 1
\```
:::
:::card
\```interact namespace bar
template stl: [{x}] ← ❌ namespace 不同,看不到 foo 的 x
\```
:::
\```
→ 嵌套不影响 namespace 规则——namespace 是隔离条件,跟嵌套无关。要跨 card 共享,两个 interact 块同 namespace。
Best practices
1. namespace 名带语义
✅ finance / bmi_calc / physics_demo / retire_planning
❌ ns1 / tmp / data / state
2. 第一块声明,后续块只引用
\```interact namespace foo
slider a 0 10 5 1
slider b 0 10 5 1
computed sum = a + b
\```
\```interact namespace foo
template stl: [{sum}] ← 只 template,不重新声明
\```
3. computed 在第一块定义
把 computed 放跟 sliders 同一块(最早)—— 后续块直接引用 computed name,不重复表达式:
\```interact namespace bmi
slider weight 40 120 70 1
slider height 1.4 2.1 1.7 0.01
computed bmi_value = weight / (height * height)
computed category = bmi_value < 18.5 ? "thin" : ...
\```
\```interact namespace bmi
template stl: [{bmi_value:.1f}] [{category}]
\```
4. 控制 namespace 范围
一个 namespace 覆盖一个章节 / 一个 demo——不要让一个 namespace 横跨整个文档。多章节用多 namespace。
See also
- Interact controls — 控件声明
- Interact text template — template 引用变量
- Computed — namespace 内派生值
- Nested DSL — namespace 跟嵌套是正交的
- Plain-text fallback principle