fix: rebuild background on theme+background update, and release word cloud shape hook - #4679
Conversation
…gether
`updateSpec` compared `background` only in the `else` branch of the theme check,
so a call that changes both `spec.background` and `spec.theme` produced
`changeTheme: true` without `reMake` / `changeBackground`. `stage.background` was
still updated through `_setCurrentTheme()`, but the background mark built from the
previous `{ fill }` spec was never rebuilt and kept covering the whole viewBox, so
nothing changed on the canvas.
`BaseMark._compileProduct` also dereferenced `option.group` bare while the two
other uses of `option` in the same method are guarded. `BaseChart.compileBackground()`
calls `this._backgroundMark.compile()` with no option, so a chart whose `background`
is a mark spec throws `Cannot read properties of undefined (reading 'group')` on the
second compile (`reCompile` without `reMake`), for example when only the legend
changes.
fix VisActor#4649
…eased The word cloud shape layout is asynchronous. When it finishes, `onLayoutFinished` pushes an `afterWordcloudShapeDraw` tap onto `stage.hooks.afterRender`, and that tap only unregisters itself once it is actually called. If the series is released in between — any `updateSpec` that triggers a reMake — `CompilableBase.release()` sets `_option` to `null`, and the next render throws `Cannot read properties of null (reading 'globalInstance')` from inside the hook, which aborts the whole render. Keep a reference to the registered tap on the series, remove it in `release()` and before registering a new one, and let the tap body tolerate a released series.
|
🦞 Aime Bot Review 感谢贡献!这个 PR 一次修复了三个相互独立的问题,描述清晰,且每个修复都配了可复现的单测,质量很高 👍 改动摘要
代码层面的观察1. 2. 3.
一个小的可选建议(非阻塞):tap 的摘除依赖 测试
测试覆盖充分,无需补充。 合并建议三个修复彼此独立、diff 克制、测试齐全,建议在 CI 全部通过后合并。当前 PR 状态为 blocked(等待检查),请确认 CI 绿灯;changeset( 再次感谢!🎉 |
There was a problem hiding this comment.
@g1f9 感谢修复!我将当前提交 547eaecdf496778b5a6dff25078769ebbe20cb6f 应用到 develop c07b139096287461be2648e2aaeae9be563925ac,使用 VRender 1.1.8 做了验证,未发现阻塞性的正确性问题,整体可以推进合并。
三个修复点均已验证:
- 同时修改 theme 与 background 后,新背景的实际画布像素正确,旧背景 mark 不再覆盖新背景;覆盖了渐变转纯色、纯色转 mark、mark 转另一个 mark,以及移除显式背景后使用 theme 背景。
- mark 形式的背景在只改图例、触发二次编译时不再崩溃,背景像素保持正确。
- 词云 series 释放或 updateSpec 重建时,待执行的钩子会被清理;释放后到达的布局完成回调不会重新注册钩子;正常绘制完成事件只触发一次并清理自身。
本地结果:现有两组测试共 55 项通过,补充的公共 updateSpec、画布像素与生命周期测试 8 项通过。
合并前麻烦处理一处工程规范调整:钩子的注册与移除请使用 VRender 已有的 tap(name, fn) / unTap(name, fn),替换直接修改 taps 数组的实现,详见行内评论。项目已有标准接口能表达同一行为,应优先复用;这里不需要额外抽象。请保留函数引用,确保只移除当前 series 的回调。
Use the standard `SyncHook.tap` / `SyncHook.unTap` API instead of pushing to and filtering `hooks.afterRender.taps` by hand, matching how `Compiler` cleans up its own stage hooks. `unTap` keeps the `fn` argument so a stage hosting more than one word cloud series only drops its own callback.
|
Thanks — switched to the standard hook API in eebadbc. // register
stage.hooks.afterRender.tap('afterWordcloudShapeDraw', afterWordcloudShapeDraw);
// remove, using the saved stage and fn
tap.stage?.hooks?.afterRender?.unTap('afterWordcloudShapeDraw', tap.fn);The Unit tests still pass ( |
Follow-up to #4679. `hooks.afterRender` is declared as `SyncHook<[IStage]>`, so a tap already receives the stage it runs on — the same way `Stage` registers its own `constructor` tap. That removes the need to remember `{ stage, fn }` on the series: the callback is a member with a stable identity, it unregisters itself from the stage it is handed, and `release()` reaches the stage through `_option`, which is still alive before `super.release()` clears it. Drops the `_afterWordcloudShapeDrawTap` field and the `_removeAfterWordcloudShapeDrawTap` helper, and moves the tap name into a single module constant so registration and removal cannot drift apart. No behavior change: the existing word cloud tap tests still cover removal on release and the absence of duplicate registrations.
🤔 This is a ...
🔗 Related issue link
fix #4649
🔗 Related PR link
🐞 Bugserver case id
💡 Background and solution
Two independent crashes/regressions, both reproduced by the unit tests added here (all of them fail on
developand pass with this PR).1.
updateSpecignoresbackgroundwhenthemechanges in the same call — #4649_updateSpeconly comparedbackgroundinside theelsebranch of the theme check:So a call that changes both produced
changeTheme: truewith neitherreMakenorchangeBackground.stage.backgroundwas still updated indirectly through_setCurrentTheme(), which is why the stage looked correct while the canvas did not change: the background mark built from the previous{ fill }spec was never rebuilt and kept covering the whole viewBox.The two checks are independent, so they are now two separate
ifs. Changingbackgroundalone already impliedreMake, so this only makes the combined case behave like the background-only case.2.
Cannot read properties of undefined (reading 'group')when recompiling a mark-spec background — the "additional observation" in #4649BaseMark._compileProductguardsoptionon two lines but dereferences it bare on a third:BaseChart.compileBackground()is the one caller that passes no option (this._backgroundMark.compile()). A chart whosebackgroundis a mark spec therefore throws on the second compile — thereCompilewithoutreMakepath, e.g. when only the legend changes — because the background mark's product already exists and execution reaches the unguarded branch. The first render is always safe, which is why this only shows up on update.3. Word cloud shape leaves a stale
afterRendertap behindThe word cloud shape layout is asynchronous. When it finishes,
onLayoutFinishedpushes anafterWordcloudShapeDrawtap ontostage.hooks.afterRender, and that tap only unregisters itself once it is actually called:If the series is released in that window — any
updateSpecthat triggers a reMake —CompilableBase.release()sets_optiontonull, and the nextstage.render()runs the stale tap and throwsCannot read properties of null (reading 'globalInstance')from inside the hook, aborting the whole render. The tap also stays registered forever when it is never called.The series now keeps a reference to the registered tap, removes it in
release()and before registering a new one, and the tap body tolerates a released series.📝 Changelog
spec.backgroundandspec.themechange in the sameupdateSpeccall; fix a crash when recompiling a chart whosebackgroundis a mark spec; fix a crash and a leakedafterRenderhook when a word cloud shape series is released while its layout is still running.updateSpec中同时修改spec.background与spec.theme时背景不刷新的问题;修复图表background为 mark 配置时二次编译崩溃的问题;修复形状词云布局未结束就释放 series 导致的崩溃与afterRender钩子泄漏。☑️ Self-Check before Merge