常见陷阱(gotchas)
生成/修改工程前扫一遍本表可以省掉大多数返工。每条:症状 → 正确写法 → 详见。
页面与布局
| # |
陷阱 |
正确写法 |
详见 |
| 1 |
播放留黑边:fullscreen-mode 内核缺省是 scaling(contain),视口非 16:9 就有黑边(编辑器新建模板/子页已默认 fill-uniform,但手写/生成 JSON 不经过模板) |
每个 type:"screen" 节点(含 screens[]/前景/背景层)显式写 fullscreen-mode:默认 fill-uniform + fill-auto-expand;不要用 stretch(会变形) |
envelope.md §4.1、visual-style.md §3 |
| 2 |
编辑器属性面板行为不对:缺 layout-profile |
每个 screen 必写 layout-profile(canvas-scale/canvas-percent/flow-fluid,与铺满配方对应) |
visual-style.md §3 |
| 3 |
流式内容跑到最右边:.vp-container.fluid 的 flex-wrap 由 fluid-wrap 选项控制、默认 wrap,根页面 fluid+column 内容超高会横向换列 |
根页面流式必写 "fluid-wrap":"nowrap";行高 px+adaptive 之和不超视口;能用 static 优先 static |
visual-style.md §3 |
| 4 |
组件全部叠在左上角 |
静态布局下每个组件必须显式写 position + size(流式子组件改用 fluid-*,无须 position/size) |
envelope.md §3/§5 |
| 5 |
padding 写成 CSS 顺序不生效 |
各组件顺序不同:页面/容器/data-repeater 的 padding 是 [上,下,左,右](非 CSS 的上右下左),且只在 fluid 布局生效(栅格用独立的 grid-padding,同为 [上,下,左,右]);basic-button 的 padding 是 CSS 序 [上,右,下,左];basic-paragraph/basic-markdown 的 text-padding 是 [左,右,上,下]——以 schema 的 vector<…> 类型串为准 |
widgets/base-options.md、widgets/text.md、widgets/controls.md |
数据与绑定
| # |
陷阱 |
正确写法 |
详见 |
| 6 |
用了不存在的 "static" 连接器类型,导入被当未知类型 |
内联静态数据用 connectorType:"json";实时演示用 mock(不要把造数脚本塞进 api.config.script);pocketbase 依赖部署环境,生成工程时不要用 |
data.md |
| 7 |
绑定不出数:三元组 uid 对不上 |
[[connUid, tableUid, fieldUid]] 三者必须与 connections/connectionData 逐字一致;connections[].tables[].uid === connectionData[connUid][i].tableId;rows 键名 == 字段 uid |
data.md 开篇 + §3 |
| 8 |
表格显示的是示例数据 |
basic-table 默认绑私有示例数据(__vp_private__),要重绑 table-data-fields 到真实三元组 |
widgets/tables.md |
| 9 |
动态数据项导入报错 ... is not valid JSON |
dynamic-prop-entries[].name 与 dynamic-prop-fields 的键必须是选项路径数组的 JSON 串:"[\"opacity\"]",不是裸 "opacity" |
widgets/data-interactions.md §0.3 |
| 10 |
条件样式不生效 |
conditionalStyles 写在节点顶层(与 options 平级),ruleId 必须命中 dataRules[].uid |
widgets/data-interactions.md §0.4 |
| 11 |
联动/选中传不动 |
发起方 *-emit、接收方 *-receive 都要开;cross-filter 只看接收组件自身开关。选中传递:basic-table(及 map-2d)自带 onSelection,自身 select-sync-receive 即可(表格还需 select-sync-fields);图表/列表/排行等其余接收方靠父容器广播——运行态(/view、SDK)页面级开关无效,须把它们包进开了 select-sync-receive 的 group-panel |
widgets/data-interactions.md §0.2 |
builder 型组件(地图 / 组态 / 三维)
容器与交互
| # |
陷阱 |
正确写法 |
详见 |
| 17 |
data-repeater 模板放错位置 |
模板放节点顶层 unitTemplate(type 必须 group-panel),不放 widgets;模板内子组件用普通字段绑定(自动取本行) |
widgets/containers.md §5b |
| 18 |
循环卡片内交互命不中 |
模板内自指动作 element 必须留空 [](写模板 uid 会指向模板而非派生实例);要带行数据用容器自身的 itemClicked |
widgets/containers.md §5b |
| 19 |
transfer 动作匹配不到目标 |
不要带 start/delay/duration(会并入触发匹配 options) |
interactions.md |
| 20 |
同一触发器写了多个 group 只生效一个 |
groups 按 trigger 内容匹配:同名同 options 只写一项,其下挂多条交互 |
interactions.md |
| 20b |
按钮点了没反应 |
trigger 写成了该组件没有的名字。itemClicked 只属于「有选中项」的数据组件(图表/表格/列表/下拉/日历/轮播/循环容器/导航…);按钮、文本、图形这类要用通用的 click。写错不会报错,只是永远不触发 |
各组件文档的「触发器」表;schema 里每个 widget 的 triggers 字段 |
| 20c |
页面级触发器静默不跑 |
timer/screenAppear/conditionMeet 只能写页面节点;但页面/窗口元素只在编辑器内预览经 prepareScreen 初始化监听——发布 /view、独立 viewer、SDK createVpViewer 都不走 starTriggertListen(_listenTriggers 恒空)→ 即使写对页面也不触发;页面/窗口上的 receiveBehaviorEvent 与页面级 *-receive 开关同样失效。周期任务改用 api 连接 refreshInterval、SDK setInterval+ctx.refreshData() 或扩展脚本 setInterval;事件接收写在组件上 |
interactions.md 触发器清单下注 |
| 20d |
交互动作 delay 没生效/顺序反直觉 |
start 不写 = 串行新批次:后项等前项 delay+duration 跑完才开始;要并行必须显式 start:"meanwhile" 并入前批 |
interactions.md |
VP-TSX 环路(写 .vp.tsx 时)
| # |
陷阱 |
正确写法 |
详见 |
| 21 |
编译报 React is not defined |
文件头 /** @jsx h */ /** @jsxFrag Fragment */ 两行 pragma 不可省 |
vptsx.md |
| 22 |
live apply 被拒:多余 import |
全文件只允许一条 import(runtime);循环/常量/函数全写在文件内 |
page-live-loop.md |
| 23 |
属性名臆造(font-color/font-size/text-align…) |
属性名 = schema 的 kebab-case 原名(文字颜色在 text-font.color,背景是 background-color);拿不准查 page_get_schemas/schema |
page-live-loop.md |
| 24 |
重复 apply 报 uid 重复 / 组件被整屏替换 |
想保留/被引用的组件显式写 uid;省略 uid 自动生成 type-N 可能撞车 |
page-live-loop.md |
| 25 |
想恢复某选项默认值时删了那一行 |
删键会触发整屏替换;显式写出默认值代替删行 |
page-live-loop.md「应用语义」 |
| 26 |
离线交付含窗口引用的单屏文件,window-instance/switchWindow 悬空 |
含画面/窗口复用的工程导出整工程;增删窗口本身也要整工程 export→apply |
page-live-loop.md |