Skip to content

常见问题排查

按"症状"分类。每条都给最小可复现 + 根因 + 修法。如果新踩到坑没在这里,优先开个 issue 再到这一页加一节。

Packaging / 装包

EUNSUPPORTEDPROTOCOL: workspace:^ 装不上

@hy-bricks/* 当成普通 npm 包装(用 file: tarball 或真发布),tarball 里看到 peerDependenciesworkspace:^ 就会跪。

根因:npm pack 不认 workspace: 协议,会原样保留。pnpm pack 会自动重写成实际版本(workspace:^^0.0.0)。

修法:打 tarball 一律用 pnpm pack,会自动把 workspace:^ 重写成实际版本。

装完包 npm install 报 monaco 找不到 / reka-ui 找不到

@hy-bricks/editorpeerDependencies 列了 9 个内部 UI 依赖(reka-ui / cva / clsx / tailwind-merge / tailwindcss-animate / lucide-vue-next / tailwindcss / monaco-editor / vue),host 必须自己装。这是设计:让 host 控制版本 dedup,避免 SDK 把 UI 库打死

完整依赖清单见 packages/editor/package.jsonpeerDependencies

tarball 里看到 src/__tests__/*.spec.tspackage/src/

发布产物偏脏,本地包不会坏但 npm publish 之前必须清。

根因:package.jsonfiles 字段写了 src,把整个源码 + 测试一起 pack 进去。

修法:files 改成 ["dist", "README.md"],确认 exports 全指向 ./dist/*。各包 package.json 已统一收窄。

Runtime / typecheck

enableHyBricksDiagnostics: 'canvases' is missing in type ...

canvases 是 optional 的,这条 typecheck 不再出现。简单接入直接:

ts
enableHyBricksDiagnostics({ level: 'debug' })

createCanvasesRegistry()@hy-bricks/core 全局 instanceRegistry 的只读视图,fallback 跟 host 自己 create 的看同一份数据,不会"看不到 host 画布"

复杂多画布仍建议显式传同一个 registry(语义清晰 / 初始化顺序可控 / 防 micro-frontend 多版本 core 分叉):

ts
import { createCanvasesRegistry } from '@hy-bricks/core'
const canvases = createCanvasesRegistry()
enableHyBricksDiagnostics({ canvases, level: 'debug' })

完整字段见 devtools API

Type 'string' is not assignable to type 'PageLayout | CanvasLayoutConfig'

ts
// ❌ 旧 schema:layout 写成字符串
const doc: PageDocument = { layout: 'free', ... }

// ✓ 用 helper 一行(canvas 字段、rootLayout 都自动兜底)
import { createMinimalPageDocument } from '@hy-bricks/canvas'
const doc = createMinimalPageDocument({ canvasWidth: 1280, canvasHeight: 720 })

SDK 暴露 createMinimalPageDocument / createFreePageDocument 工厂,直接输出 normalize 后的 v1 形态(canvas: { width: { mode:'px', value }, height: { mode:'px', value } },CanvasSizeValue 协议)。createFreePageDocument 是 free 顶层布局的快捷版。

后端 getRender 返回的 payload 已经 normalize 过,host 直接喂 Renderer 即可;只有自己手造 doc(测试 / smoke / demo)用 helper。

修改 handle.getInstance(id) 返回值后 SDK 内部不变了

getInstance / getInstanceList / getBreadcrumb 全是 深拷贝口径:host 改返回对象的嵌套字段(props.x = 2 / rect.x = 99 / layoutBox.width = ...)不污染 SDK 内部 reactive。

要真改文档:走 handle.dispatch({ type: 'updateInstance', ... }),见 CanvasHandle

组件滚动 / 重渲染后,用户选值 / 查询条件变回默认

症状:看板里的组件,用户改了选值(如维度选成"年"),滚出视口再滚回、或页面重渲染后,选值又变回默认("日"),查询条件也丢。

两条独立根因,0.6.3 分别处理:

  • 重渲染冲刷(已默认修复,无需开关):渲染器每次重渲染都传"内容相同、引用不同"的 custom 值对象,老 watch 无条件写回文档默认,冲掉用户运行时改的值。0.6.3 改为 diff 同步(Object.is 比上次 prop,真变才同步)—— 所有宿主常开,升到 0.6.3 即生效。
  • 滚出视口被拆重建(opt-in):视口虚拟化把滚出超约 1.5s 的实例真 dispose 卸 vm,滚回重建 data() 重跑、选值复位。给 Renderer / Designer 传 :persist-instance-state="true" 开启快照持久化(dispose 前拍 $data 快照、重建还原)。或更直接:0.6.5 / 0.6.6 起给 :scheduler-options{ disposeOnExit: false }{ disabled: true } 让它根本不卸载(从根上不重建、也不重取,代价是已挂常驻内存)—— persistInstanceState 是"卸载 + 还原",这俩是"不卸载"。各档见 调度器 · RenderSchedulerOptions

注意区分:隐藏(instance.hidden,v-show)是另一回事,由 keepHiddenMounted(默认开)保活,vm 不拆、不需快照。详见 渲染一个页面 · 组件滚出视口再滚回

已知限制(开 persistInstanceState 时):

  • 只保护同步状态。组件若在 mounted异步重置状态(fetch().then(set)),会盖过还原值、选值仍丢 —— 别在 mounted 盲目异步重置被持久化字段。
  • instanceId 必须逻辑唯一、不可删后复用:删掉实例后用相同 instanceId + componentId 新建会继承陈旧快照(快照仅画布 dispose / LRU 满 200 才清)。

样式

装了 @hy-bricks/canvas 但选中框 / runtime wrapper 没样式

老版本 canvas 输出独立 dist/canvas.css,host 忘 import 就丢样式。当前版本 canvas SFC <style> 已通过 vite-plugin-css-injected-by-js 内联进 JS bundle,host 不需要 import css,运行时自动生效。

如果还是丢样式,检查:

  • 用的 @hy-bricks/canvas 是不是含 inline 样式注入的版本
  • 有没有手动屏蔽 inline <style> 注入

@hy-bricks/editor 装了但 shadcn 主题没生效

editor 的 SFC <style> 已注入 JS bundle,但 tailwind utility / shadcn CSS variable 是独立的 dist/style.css(tailwind CLI 编译产物,无法注入)。host 必须:

ts
// main.ts
import '@hy-bricks/editor/style.css'

而且 host 的 tailwind.config.jspresets: [hyBricksPreset],见 快速开始

devtools 浮窗看不到 / 显示但样式 broken

devtools 浮窗 CSS 在 dist/index.{mjs,cjs} + dist/auto.{mjs,cjs} 双 entry 都注入。看不到先确认:

  • 是不是 DEV 模式?prod 不会自动启
  • app.use(createHyperCard({ ... })) 是不是在 enableHyBricksDiagnostics 之前调?(canvases 注册顺序)
  • 是不是 import '@hy-bricks/devtools/auto' 但忘了先 createHyperCard?

拖拽 / 选中

Designer 里点容器子项点不中蓝框

较早版本存在此问题:选中实例 DOM 在正确位置但被父 stacking context 压住。修法:升 SDK 到包含 CanvasStageisolation: isolate 的版本(InteractionLayer z-index 9999 在 stage 内生效)。

Flex / Grid / Split 容器里子项拖不动 / 拖了不动

子项是 layout-managed,位置由父布局算,直接 drag 会被 SDK 拒。SDK 会触发 handle.cannotDragLayoutManagedChildEvent,devtools collector 自动 watch 进 recentDragFailures

host 可以监听做 toast 提示;真要改位置走 updateLayoutItem(id, patch)(改子项 layout 参数)或换 reparent 到 free 容器。见 跨容器移动

slot 加第 4 个子项被拒 + reason 'max-children-exceeded'

SlotDecl.maxChildren 守门。组件契约里声明的 slot 上限到了,canReparent dry-run 和真 drop 都会拒,host 收到 cannotReparentEvent.reason === 'max-children-exceeded',toast 提示用户即可。优先级低于 multiple: false(单 slot 排他先拒)。

Build / bundle

dist/index.js 巨大(几 MB)— monaco / element-plus 全打进 bundle

那是 host 端的 build chunk warning,不是 SDK 问题。SDK 自身把这些重 dep 都 external 出去了,host 自己的 build config 才决定怎么打。生产 host 一般用 build.rollupOptions.output.manualChunks 把 monaco / vue / element-plus / @hy-bricks 拆成单独 chunk。